Related question here but I am not sure how to adapt the solution to this problem.
I am trying to create a reusable component for a landing page with tabs. Each tab is a child of the reusable component and has its own store defined as a prop:
<LandingPage>
<LandingPage.Tab store={store1}/>
<LandingPage.Tab store={store2}/>
...
<LandingPage.Tab store={storeN}/>
</LandingPage>
I'd like to fetch data from every tab's store when the parent component mounts to allow quick switching between tabs. Inside the componentDidMount function, I iterate over each child and assign the onChange callback for the child's store to an anonymous arrow function:
var LandingPage = React.createClass({
getInitialState: function () {
return {
data: [] /* each index will be an array of data for a different tab */
};
},
componentDidMount: function () {
var self = this;
React.Children.forEach(this.props.children, function (child, index) {
child.props.store.onChange(() => {
self.setDataAtIndex(index, child.props.store.getData());
});
});
},
setDataAtIndex: function (index, newData) {
var data = this.state.data.slice();
data[index] = newData;
this.setState({
data: data
});
},
...
});
However, when the page first loads, I get a warning message from React:
Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component. This is a no-op. Please check the code for the LandingPage component.
I am confused because I thought I could assume the component is mounted if I am inside the componentDidMount function. This warning message goes away when I refresh the page.
Can someone explain this behavior and tell me how to correctly structure code to eliminate the warning message?
This function...
() => { // this calls setState
self.setDataAtIndex(index, child.props.store.getData());
}
Will be called every time a tabs store changes whether the LandingPage
component is mounted or not. That is a problem. You need to tell the stores to stop calling this function when LandingPage
unmounts. Without modifying the store, you could just override the change listener with a no-op, like this...
componentWillUnmount: function () {
var self = this;
React.Children.forEach(this.props.children, function (child, index) {
child.props.store.onChange(() => {});
});
}
Now, when the component isn't mounted, () => {}
should be called instead, which doesn't call setState
and is therefore harmless