Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

setState not triggering a re-render when data has been modified

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/

like image 447
Joel Avatar asked Apr 05 '15 16:04

Joel


1 Answers

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.

like image 94
Derek Avatar answered Sep 25 '22 05:09

Derek