React: loading and updating nested resources when URI changes without excess duplication? - javascript

Let's say I have a nested URI structure, something like the following:
http://example.com/collections/{id}
http://example.com/collections/{collectionId}/categories/{id}
http://example.com/collections/{collectionId}/categories/{categoryId}/book/{id}
I can use react-router to render the correct component on page load, and when the URI changes.
Let's take the first case:
http://example.com/collections/{id}
Let's assume we have a CollectionShow component.
When the component first loads, I can pull the collection ID out of the URI and load the correct collection:
componentDidMount () {
this.loadCollection(this.props.match.params.id);
}
(Assume that loadCollection loads a collection with an AJAX call and sets it into the component's state.)
However, when the URI changes (through, e.g., the user clicking on a <Link>, react-router doesn't entirely re-build the component, it simply updates its props, forcing it to rerender. So, in order to update the compomnent's state, we also need to update the state on update:
componentDidUpdate(prevProps) {
if (!this.state.collection || this.collectionDidChange(prevProps)) {
this.loadCollection(this.props.match.params.id);
}
}
collectionDidChange(prevProps) {
return String(prevProps.match.params.id) !== String(this.props.match.params.id)
}
So far so good. But what about the second URL?
http://example.com/collections/{collectionId}/categories/{id}
Let's assume we have a CategoryShow component.
Now we don't only have to consider the collectionId changing, but also the category ID. We have to reload the collection if that ID changes, and we also have to reload the category if that changes.
The problem compounds with a third-level nesting (a BookShow component). We end up with something like this:
componentDidUpdate(prevProps) {
if (!this.state.collection || this.collectionDidChange(prevProps)) {
this.loadCollection(this.props.match.params.collectionId);
}
if (!this.state.category || this.collectionDidChange(prevProps) || this.categoryDidChange(prevProps)) {
this.loadCollection(this.props.match.params.collectionId)
.then(() => this.loadCategory(this.props.match.params.categoryId);
}
if (!this.state.book || this.collectionDidChange(prevProps) || this.categoryDidChange(prevProps) || this.bookDidChange(prevProps)) {
this.loadCollection(this.props.match.params.collectionId)
.then(() => this.loadCategory(this.props.match.params.categoryId)
.then(() => this.loadBook(this.props.match.params.id);
}
}
Not only is this unwieldy, it also results in a fair amount of code duplication across the three components, CollectionShow, CategoryShow and BookShow.
Using redux won't help matters much, because we still have to update the global state when the URI changes.
Is there a clean, efficient, React-friendly way of handling updates of nested resources such as these?

You could create a CollectionPage component that handles all the AJAX calls and keeps data in state.
This could pass down the collection, category/categories and books to the components (CollectionShow, CategoryShow and BookShow).
In CollectionPage you could use componentDidUpdate and componentDidMount as you presented it.
Your <*>Show components will know nothing about props.match.params.* and will only get the data needed to render the wanted content.
CollectionPage can be use for all your routes or you could change the route to something like
/collections/:collectionId?/:categoryId?/:bookId?
making all params options. You can check for the available ids in CollectionPage.
Hope it helps!

If I understood your problem it is something architectural. The parent component is the one that should be doing this management and injecting the result through subcomponents. Split your component in small components and render each one accordingly.
The code you shared will be splint in 3 others
The mponentDidUpdate(prevProps) method will go to the parent component simply as a componentDidMount().
Then if the router changes the component will be recreated and the new values will be sent across the modules.
If you dont wanna split you code you should at least do the step 2.
//everytime you get to the router this will be triggered and depending of the parameters of your router, you get the values you need and set the state
componentDidMount() {
if (!this.state.collection) {
this.loadCollection(this.props.match.params.collectionId);
}
if (!this.state.category) {
this.loadCollection(this.props.match.params.collectionId)
.then(() => this.loadCategory(this.props.match.params.categoryId);
}
if (!this.state.book) {
this.loadCollection(this.props.match.params.collectionId)
.then(() => this.loadCategory(this.props.match.params.categoryId)
.then(() => this.loadBook(this.props.match.params.id);
}
}
render() {
return (
//you can add conditions to render as well
<CollectionComponent {...this.props} {...{
collection: this.collection
}} />
<CategoryComponent {...this.props} {...{
categ: this.categ
}} />
<BookComponent {...this.props} {...{
book: this.book
}} />
)
}

Related

How to deal with duplicated function code that alters parent state

Say we got a Page-component that delegates the rendering of notifications to a Notification-component. The Page-component's render method contains the following ...
{this.state.notifications &&
<Notifications
notifications={this.state.notifications}
removeNotifAt={index => this.setState(prevState => {
const copy = [...prevState.notifications]
copy.splice(index, 1)
return { notifications: copy }
})}
removeNotifyBy={id => this.setState(prevState => {
const copy = [...prevState.notifications]
const index = copy.findIndex((notif, _) => { return notif.id === id })
copy.splice(index, 1)
return { notifications: copy }
})}
/>
}
... as you might notice, Notifications require some rather large function to alter the state of its parent. Since they access this.state, these functions have to be defined in the parent of Notifications, in this case Page.
Now, one can imagine that multiple pages have notifications that needs rendering and so they all have to code-duplicate the code snippet above. As we all know, code-duplication is bad, so how can we best avoid it?
It's impossible to extract the functions removeNotifyAt and removeNotifyBy out into functions defined in, say, Notifications.js since they need to access this.state.
So, what's the react-way of dealing with such duplicate functions that you can't extract away because it needs to access this.state? I suppose I am not the first one stumpling upon this, giving how trivial of a case this is.
You can move the code from removeNotifyBy and removeNotifyBy into functions, and place them in the parent component. Then you can pass them into the child components as props.
You could extract those functions into Notifications.js. (That is in case you don't wish to write those functions inside parent component).
Both the parent's state as will as the function that sets the state can be passed as props to Notifications component.
Example:
<Notifications parentState={this.state} parentStateHandler={this.setState} />
//Note: Ideally props shouldn't be named like this and one must avoid passing entire state object as prop, rather you should split it into props that the child component requires. However this is just to give you the clarity regarding how to access parent's state in child
Now Notifications component has entire parents state which can be accessed by props.parentState and you can also set Parent state by using props.parentStateHandler instead of this.setState

next/link losing state when navigating to another page

I am developing a library Next.js application. For the purposes of this question, I have two pages in my application: BooksPage which lists all books, and BookPage which renders details of a book. In terms of components, I have a <Books /> component which renders a <Book /> component for every book in my library database.
Here are my components:
Books.js:
function Books({ books }) {
return (
<>
{books.map(book => (
<Book key={book.id} book={book} />
))}
</>
);
}
Book.js:
class Book extends React.Component {
constructor(props, context) {
super(props, context);
this.state = { liked: false };
}
like = () => {
this.setState({ liked: this.state.liked ? false : true })
};
render() {
return (
<>
<Link href={`/books/${book.slug}`}>
<a>{book.title}</a>
</Link>
<Button onClick={this.like}>
<LikeIcon
className={this.state.liked ? "text-success" : "text-dark"}
/>
</Button>
</>
);
}
}
Problem:
Say that I am on page BooksPage. When I click the like button of a <Book /> the icon color toggles properly in the frontend and the like is successfully added or removed in the backend. When I refresh BooksPage all the state is maintained and consistent.
The problem arises when I like something on BooksPage and then immediately navigate to BookPage without refreshing using next/link. There the like button is not toggled consistently and the state from BooksPage is lost. Notice that if I hard-refresh the page everything goes back to normal.
Slow solution: Do not use next/link.
Replace
<Link href={`/books/${book.slug}`}>
<a>{book.title}</a>
</Link>
with
<a href={`/books/${book.slug}`}>{book.title}</a>
Fast solution: Keep using next/link?
Is there a way to use next/link and maintain state when navigating to another pre-rendered route?
TLDR: Any time the button needs to be changed, the API must change data, and it must update the closest parent's local state to update the button's appearance. The API will control all aspects of local state. You can't update local state unless an API request is successful. Therefore, the client and API are always 1:1.
The Button component in Book.js should NOT be maintaining its own state separately from the API data; instead, wherever you're fetching book data from the API, it should also be controlling the button's state (and its appearance). Why? Because with the current approach, the API request can fail, but the client will still update. As a result, the API and client may no longer be 1:1 (client shows liked, but API still shows that it's disliked/unliked).
In practice, you'll have a parent container that acts like a state manager. It fetches all relevant data, handles updates to the data, and displays the results using stateless child components. Any time a child component needs to be updated (such displaying a "like" or "dislike" button based upon a button press), it must first make an API request to change the data, then the API must respond with relevant data to the update the state used by the child:
Alternatively, if this component is reusable, then you'll conditionally render it using this.props.book (which comes from a parent container) or the child component must request data from an API to update its own local this.state.book. Same as the above diagram, the API requests control all aspects of state changes:
There's yet another approach that is the same as the diagram above, but it uses the child local state regardless of the parent's state. This child state will only be updated by its own class methods. This introduces a bit more complexity because you have to make sure that any parent state changes won't rerender the child component with stale data. Ultimately, which approach to take is up to you.
On a side note: This question doesn't make much contextual sense as libraries don't render pages nor do they attempt internal page navigations. Instead, they offer reusable components that can be utilized by one or many NextJS project pages or components.
You need to use Model.refresh_from_db(...)--(Django Doc) method to retrieve the updated value from the Database
class DeleteLikeView(APIView):
def post(self, request, book):
book = get_object_or_404(Book, id=book)
print(book.num_of_likes)
like = Like.objects.get(user=request.user, book=book)
like.delete()
book.refresh_from_db() # here is the update
print(book.num_of_likes) # You will get the updated value
return ...
Pass the liked property of a book somehow through the API. Then, pass that prop down from the Books component to the Book component.
Add a componentDidUpdate() method to your book component.
class Book extends React.Component {
constructor(props, context) {
super(props, context);
this.state = { liked: this.props.liked };
}
componentDidUpdate(prevProps) {
if (this.props.liked !== prevProps.liked) {
this.setState({
liked: this.props.liked,
});
}
}
like = () => {
this.setState({ liked: !this.state.liked })
};
render() {
return (
<>
<Link href={`/books/${book.slug}`}>
<a>{book.title}</a>
</Link>
<Button onClick={this.like}>
<LikeIcon
className={this.state.liked ? "text-success" : "text-dark"}
/>
</Button>
</>
);
}
}
In DeleteLikeView class you get book object. it retrieved from DB and saved in book variable.
when you deleted like object num_of_likes attribute has been updated in DB but your variable still consists previous object. after like.delete() command you should get object again and print num_of_likes att. It is as your expected.

How to avoid unnecessary re-render for "static components" in react functional component?

I have a react functional component that shows list of tags and posts + few static text/decorations. I store the currently selected tag in a state using useState hook. Posts are fetched by using apollo's useQuery hook with tag variable. User should able to select a tag and it will replace the current tag state - thus the useQuery(POSTS_QUERY) will re-run with new tag variable.
const onTagSelectChange = (window: Window,
router: NextRouter,
name: string,
checked: boolean,
tagSetter: React.Dispatch<React.SetStateAction<string>>) => {
if (checked) {
setTagQueryInUrl(window, router, name)
tagSetter(name)
} else {
setTagQueryInUrl(window, router, null)
tagSetter(null)
}
}
const NewsList: NextPage = () => {
const router = useRouter()
const query = router.query as Query
// store tag in state
// initialize tag from `tag` query
const [tag, setTag] = useState(query.tag)
const { data: postsData, loading: postsLoading, error: postsError } = useQuery(
POSTS_QUERY,
{
variables: {
tag: tag
}
}
)
const { data: tagsData, loading: tagsLoading, error: tagsError } = useQuery(TAGS_QUERY)
// show error page if either posts or tags query returned error
if (postsError || tagsError) {
return <Error statusCode={500} />
}
return (
<div>
<h1>Here we have list of news, and I should not re-render everytim :(</h1>
<Tags
loading={tagsLoading}
data={tagsData}
isChecked={(name) => name === tag}
onChange={(name, checked) => onTagSelectChange(window, router, name, checked, setTag)}
/>
<Posts loading={postsLoading} data={postsData} />
</div>
)
}
My question is, why is my h1 block keeps re-rendering even though I don't pass anything to it? Or do I completely misunderstand how react works?
React components re-render whenever their state or props change. If I am reading this correctly then you are changing tag in state whenever the url changes and thus making the component to re-render itself.
As your state is declared on your NewsList component, any state change (as another user stated on his answer) will trigger a re-render of the whole component (NewList) and not only to the components that you have passed your state (thus to the static <h1> you have in there).
If there are parts of this component that have nothing to do with this state, you can move them outside to avoid the re-render.
Though, on cases like this, re-rendering your <h1> is not a cost for React. You should worry and follow this approach on custom components where more complex things going on (e.g. populating lists or calculating stuff etc..). In those cases, you don't want all this complex stuff to happen again, if they are not affected by a parent's state change. You should also always consider, if moving the component outside makes sense or by doing so, you make your code complex.
You should always strike a balance between well-organized and efficient code.

Share data between React components with no relation?

I'm working on a React component library that allows for client-side data filtering by passing an array of objects and an <input/> as props to a <SearchFilter/> component. I want to return the filtered results to a separate <SearchResults/> component that can be rendered elsewhere in the tree (i.e. the results component doesn't have to be a child of the input component).
I've got the filtering figured out, but I'm not sure the best route to take in React on getting the filtered data to the <SearchResults/> component.
This is what I'd like to end up with...
<SearchFilter
data={data}
input={<input type="text" value={value} onChange={this.handleChange}/>}
/>
Then, using Render Props to return the data and map over that to return JSX, there would be the results component. Something like this...
<SearchResults
render={data => (
data.map(el => (
<div>
<span>{data.someProperty}</span>
</div>
)
)}
/>
This is what I'd like to achieve because I want to allow for rendering the <SearchFilter/> component at one place in the tree, and allow the <SearchResults/> component to be rendered elsewhere, so that there's maximum flexibility in how the tree is composed and, therefore, how the view is rendered.
I've looked into the Context API, but it seems like that would require a handful more components to be a part of my library, which further complicates what I'm trying to achieve. If that's the only way around it, then that's fine, but I wanted to ask and see if anyone can think of another solution.
Thanks!
The bigger issue is that you will need to manage a state that is shared between components on a higher level, i.e., any component that will wrap these other two components, ultimately. With plain React, this state would be managed by the parent (or ancestor) component, passing down the relevant values as props. This opposed to the, usually bad, idea to have sibling components influence each other's state, since you well get into the "who's boss here"-problem.
The thing the Context API handles is not having to pass down props for things that typically don't change (or: typically shouldn't cause renders to trigger often).
A global state store, such as Redux, can help you modelling this, but in essence it's not much more than 'a' component managing state, and other components rendering according to that state. Events within the lower components trigger changes in the data, which will cause the state to change, which will cause the props of the children to change, which then will cause re-renders.
I'd advise you to try using this simple pattern:
class Search ... {
state = {data: [], text: ""}
render() {
return (
<div>
<SearchFilter
data={this.state.data}
onSearch={() => this.fetchNewData()}
onChange={(e) => this.setState({text: e.targetElement.value})}
text={this.state.text}
/>
<SearchResults data={this.state.data} />
</div>
);
}
fetchNewData() {
fetch('/url?text=' + this.state.text)
.then((newData) => { this.setState({data: newData}); })
}
}
Something along these lines. If you have trouble modelling stuff like this, you can use Redux to force you to do it in a similar way, and avoid managing local state intermixing with global state (which is typically something that is hard to manage).
If you do this right, components that have no state (i.e., aren't responsible for managing state and thus have no event handlers) can all become pure components, i.e. stateless components, i.e. functions that return JSX based on props:
const SearchResults = ({data}) => <div>{data.map( () => <etc /> )}</div>
You could create a data store class that holds your filter, pass it in as a property to both components, and have your SearchFilter component change a value in that.

React.js: Parent state values not passing into child properties + Fetch API data cannot be accessed

I am encountering several issues in a very basic color harmony picker I am developing. I am still a beginner in React and JSX. I initially had it put up on GitHub so the full files are on there, but I moved it over to Codepen instead.
Here is the Codepen
I made a lot of comments so sorry if they're a bit much, but hopefully they help. My problems don't begin until line 41, the displayHarmonies() method of the DataStore class. The values passed to it come from my App (parent) component:
displayHarmonies(color, harmony) {
//color and harmony pass in dynamically just fine...this.data will not return anything, not even "undefined"
console.log(color + " is the color and " + harmony + " is the harmony...and dataStore.displayHarmonies says: " + this.data);
this.registeredWatchers.map((watcher) => {
let result = "not green"; //result and resultHex will be determined with an underscore statement that will associate the color & harmony choice (primary + foreign key concept) and will return correct harmony color(s)
let resultHex = "#HEX";
appState.harmonyColor = result;
appState.harmonyHex = resultHex;
//call to app component's onDataChange() method, where new states will be set using the the appState data we just set in lines 49 and 50
watcher.onDataChange();
})
}
As you can see from my first comment, the only part that doesn't log to the console is this.data, which is set in the constructor for the DataStore:
constructor(data) {
//store that data in the object
//data is not being received from object instance of dataStore on line 187
this.data = data;
On line 187 I make an instance of the DataStore and pass it a variable named data. Prior to being used, this variable is initialized and then assigned to parsed JSON data via Fetch API:
let data = [];
//use polyfill for older browsers to do Ajax request
fetch("data/data.json").then((response) => {
//if we actually got something
if (response.ok) {
//then return the text we loaded
return response.text();
}
}).then((textResponse) => {
data = JSON.parse(textResponse);
});
If I console out the data in the second fetch .then() method, the JSON comes back just fine. As soon as I try to use the data variable anywhere else in the application, it returns nothing, as shown in the displayHarmonies() method's console.log(). So that's my first issue, but before I wanted to get to that, I wanted to solve the other issue I was having.
After the appState object (initialized prior to the DataStore, under the fetch statement) values get set to the result variables, displayHarmonies() runs watcher.onDataChange() (in the App component/parent) where the harmonyColor and harmonyHex states get assigned to the new appState values:
onDataChange() {
console.log("onDataChange() in App called");
this.setState({
harmonyColor: appState.harmonyColor,
harmonyHex: appState.harmonyHex
})
}
If I log these states out to the console, they are the right values, so that's not the problem. I then pass my states to the Display child component to be used as properties:
<Display colorChoice={this.state.currentColor} harmonyChoice={this.state.currentHarmony} harmonyColor={this.state.harmonyColor} harmonyHex={this.state.harmonyHex} />
I then set the Display component states in the constructor, assigning them to the props that are being sent to it with each new rendition of the application. I then display the data onto the DOM with the Display component's render method. What's odd is that the application will display the initial states (color: red, harmony: direct, harmonyColor: green, etc.) just fine, but as soon as a change is made, the data on the DOM does not update. The initial data is loaded in the same way though: by passing the parent's states into the child's properties. I have a few console.log()s in place that seem to prove why this should work, however, it does not. So what am I doing wrong?
Thanks, and hope this is not too much for one question!
First a bit to your current code, at the end of the post, I have added an alternative solution, so if this is tl;dr; just skip to the snippet at the end :)
A first remark would be on the data variable that you wish to pass on to your DataStore, nl (I left out some parts, as they are irrelevant to the discussion)
let data = [];
fetch("data/data.json").then(( response ) => {
data = JSON.parse( response.text() );
});
//... later down the code
var store = new DataStore(data);
Here you are reassigning the data variable inside the then promise chain of your fetch call. Although the assignment will appear to work, the data that now is on store.data will be an empty array, and the global variable will data will now contain the parsed response.text(). You should probably just push in the data you have just parsed (but in my example, I didn't even include the DataStore so this is just for future reference)
In your CodePen, you seem to mixing props & state for your Display component. That is in essence a no-op, you shouldn't mix them unless you really know what you are doing. Also note, that by calling this.setState inside the componentWillReceiveProps life cycle method, the app will automatically re-render more than needed. I am referring to this code:
componentWillReceiveProps(nextProps) {
this.setState({
color: nextProps.colorChoice,
harmony: nextProps.harmonyChoice,
harmonyColor: nextProps.harmonyColor,
harmonyHex: nextProps.harmonyHex
});
}
But you are then rendering like this:
render() {
return (
<div>
{/* these aren't changing even though states are being set */}
<p><b>Color:</b> {this.state.color}</p>
<p><b>Harmony:</b> {this.state.harmony}</p>
<p><b>Harmony Color(s):</b> {this.state.harmonyColor} ({this.state.harmonyHex})</p>
</div>
)
}
Here you should remove the componentWillReceiveProps method, and render values from this.props as you are passing these along from your App.
Alternative solution
As mentioned in the comments, your code currently is doing a lot more than it should do to pass state between parent and child components.
One thing you should keep in mind, is that when a component state gets changed, react will re-render the component automatically. When it sees that the virtual DOM has discrepancies with the real DOM it will automatically replace those components.
In that sense, your DataStore is not necessary. Depending on how you want to manage state, the component will react on those changes.
Since your app uses Component State (which is fine for small applications, once you want to move to bigger applications, you will probably want to move on to something like Redux, or MobX), the only thing you need to do, is to make sure that you set the correct components state to trigger the rendering.
As an example, I remade your code in a cleaner way:
const Choice = ({ header, values, onChange, activeValue }) => {
return <ul>
<li><h1>{ header }</h1></li>
{ values.map( (value, key) => <li
key={key+value}
className={classNames( { active: value === activeValue, item: true } )}
onClick={() => onChange( value )}>{ value }</li> ) }
</ul>
};
const colors = ['red', 'green', 'black', 'blue', 'yellow'];
const harmonies = ['direct', 'split', 'analogous'];
class App extends React.Component {
constructor(...args) {
super(...args);
this.state = {
activeColor: undefined,
activeHarmony: undefined
};
}
onColorChanged( color ) {
this.setState({ activeColor: color });
}
onHarmonyChanged( harmony ) {
this.setState({ activeHarmony: harmony });
}
render() {
let { activeColor, activeHarmony } = this.state;
return <div>
<Choice
header="Choose color"
values={colors}
activeValue={activeColor}
onChange={(...args) => this.onColorChanged(...args)} />
<Choice
header="Choose harmony"
values={harmonies}
activeValue={activeHarmony}
onChange={(...args) => this.onHarmonyChanged(...args)} />
</div>;
}
}
ReactDOM.render( <App />, document.querySelector('#container'));
h1 { margin: 0; padding: 0; }
ul {
list-style-type: none;
}
.item {
cursor: pointer;
padding: 5px;
}
.active { background-color: lightgreen; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.2/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/15.6.2/react-dom.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prop-types/15.6.0/prop-types.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/classnames/2.2.5/index.js"></script>
<div id="container"></div>
Now, there are some things in this sample code that might need some explanation. For one, this code has 2 component types, 1 presentational component called Choice which is stateless, and one container component called App which delegates it's state to it's children.
A bit more information about container & presentational components can be found on the blog of Dan Abramov (redux creator)
The essence of the above concept is just this, the App component is responsible for the state, and for sharing it with it's children. So, all state changes need to be made on the App component. As you can see in the render, the App simply passes its state along:
render() {
let { activeColor, activeHarmony } = this.state;
return <div>
<Choice
header="Choose color"
values={colors}
activeValue={activeColor}
onChange={(...args) => this.onColorChanged(...args)} />
<Choice
header="Choose harmony"
values={harmonies}
activeValue={activeHarmony}
onChange={(...args) => this.onHarmonyChanged(...args)} />
</div>;
}
The App passes a change handler along to the Choice component that can be called when a selection should occur, this gets forwarded to the App, the state changes, and app re-renders, allowing the Choice component to update it's elements.
const Choice = ({ header, values, onChange, activeValue })
Based on the props passed into it, the Choice component can decide which is the active item at the moment of rendering. As you can see, the props are destructed. header, values, onChange and activeValue are all properties on the props of the component, but to save time, we can assign these values at ones to a variable and use them in the rendering.
I tried cloning your repo, but it seems to be nested in another repo. With your current setup, this may work:
In your App component, you can put this lifecycle method to fetch the data, and then set the state with the received data.:
componentDidMount(){
fetch("data/data.json").then((response) => {
//if we actually got something
if (response.ok) {
//then return the text we loaded
return response.text();
}
}).then((textResponse) => {
this.setState({
data : JSON.parse(textResponse);
})
});
}
In the return statement, you can render the data store as a child so App can pass the data like this:
return (
<div className="App">
<DataStore data={this.state.data} />
<h1>Color Harmonies</h1>
{/* assigns this.colorChosen() & this.harmonyChosen() methods as properties to be called in Picker component */}
<Picker colorChosen={this.colorChosen.bind(this)} harmonyChosen={this.harmonyChosen.bind(this)}/>
{/* give Display component props that are dynamically set with states */}
<Display colorChoice={this.state.currentColor} harmonyChoice={this.state.currentHarmony} harmonyColor={this.state.harmonyColor} harmonyHex={this.state.harmonyHex} />
</div>
);
Then, your data store should receive the data as a prop, so you can use it like this:
displayHarmonies(color, harmony) {
//color and harmony pass in dynamically just fine...this.data will not return anything, not even "undefined"
console.log(color + " is the color and " + harmony + " is the harmony...and dataStore.displayHarmonies says: " + this.props.data); //data is received in the properties so you can use it.
//other code
})
Doing this, you should also be able to remove this.data from the constructor of the DataStore component.
Also in Data store, youll want to to allow it to accept props like this:
constructor(props){
super(props)
}

Categories