setState not triggering a re-render when data has been modified
Solution 1
I think the problem could be the your are only using getInitialState
for the Row
. You are setting state with the passed in props. If the children get new props, getInitialState
won't get called again. I always refer back to https://facebook.github.io/react/docs/component-specs.html to see all the lifecyle events. Try using componentWillReceiveProps(object nextProps)
to set the state again. If you don't actually need state
in the child, remove the use of state and just use props
and it probably will work.
Solution 2
If you're children are using State and state is not updating, what about props? does that render just fine? If the parent changes the state of the children and the children don't reflect that change those child components most likely need a unique key assigned to them which tells React that these items need to be updated when the state is updated on the parent. I ran into a similar issue where props inside the child updated to reflect the parent but the state of the child would not update. Once I added keys, the solution was resolved. For further reading on this check out this blog that initially hinted the problem to me.
http://blog.arkency.com/2014/10/react-dot-js-and-dynamic-children-why-the-keys-are-important/
There is also the official documentation from React that explains this situation.
http://facebook.github.io/react/docs/multiple-components.html#dynamic-children
Joel
Updated on July 08, 2022Comments
-
Joel almost 2 years
Just started working with React, and it's been going relatively smooth up until this point.
I have a list, that contains X pre-defined items, which means that the list is always rendered with a given amount of rows.
This data is collected from a REST API, prior to the list rendering. The data contains variables relating to the list, as well as an array that contains each item within the list.
I chose the easy route, so I populate every component with a single JSON object named 'data', that contains everything necessary.
Rendering the list looks something like this:
<MyList data={data} />
Then, in the getInitialState of MyList:
dataInt: this.props.data.dataInt || 0, dataStr: this.props.data.dataStr || '', rows: this.props.data.rows || []
So I store my array of items (JSON) in the initial state of the list (parent), and when I choose to render the list, I first create all components (children) in an array, like this:
var toRender = []; for(i = 0; i < this.state.rows.length; i++) { toRender.push(<ItemRow data={this.state.rows[i]} />); }
and then I render like this:
return ( <div className="item-container"> <table className="item-table"> {toRender} </table> </div> );
The render-function of MyItem look something like this:
return ( <tr> <td>{this.state.dataFromItem}</td> </tr> );
Now, let's say I want to modify the child data from within the parent, with some new data I just got from the API. Same structure, just the value of one field/column has changed:
i = indexOfItemToBeUpdated; var newRows = this.state.rows; newRows[i] = data; // New JSON object with same keys, different values. this.setState({ rows: newRows }); // Doesn't trigger re-render this.forceUpdate(); // Doesn't trigger re-render
What am I doing wrong?
At first I thought it was because I was wrapping the render function of MyItem in a , but since it renders perfectly fine on the initial render, I will assume that is an acceptable wrapper.
After some testing, it seems that the parent view is re-rendered, but not the children (which are based on the data that is updated within the parent).
JSfiddle: https://jsfiddle.net/zfenub6f/1/