Reputation: 14824
I have my component that populates an array that determines what will be displayed in the render
function (obviously the componentDidMount
function)
But nothing happens when it updates the this.state.containers
.. I've put a console.log
into the first render
loop, which successfully displays:
But then nothing changes on the client side so I don't know if my loops are bad but I get no JS errors. So I'm not sure what the problem is, pretty new to React so any help would be great thanks.
var AdminForumContainer = React.createClass({
getInitialState: function() {
return { containers: [] }
},
componentDidMount: function() {
$.ajax({
method: 'GET',
url: '/admin/manage/forum/populate',
success: function(data) {
var {containers} = this.state;
for (var i = 0; i < data.length; i++) {
containers.push(data[i]);
}
this.setState({containers});
}.bind(this)
})
},
createMainThread: function(containerid) {
},
createSubThread: function(containerid) {
},
render: function() {
return (
<div>
{this.state.containers.map(function(container) {
{console.log(container)}
<table key={container.containerid} className="containers">
<caption>{container.containername}</caption>
<thead className="containerTitle">
<tr>
<td colspan="2">Main Threads</td>
</tr>
</thead>
<thead>
<tr>
<td>Thread Name</td>
<td>Delete</td>
</tr>
</thead>
<tbody>
{
container.mainthreads.map(function(mainthread) {
return (
<tr key={mainthread.threadid}>
<td>{mainthread.threadname}</td>
<td><button className="button alert">Delete</button></td>
</tr>
)
})
}
<tr>
<td><input type="text"/></td>
<td><button className="button">Create</button></td>
</tr>
</tbody>
<thead>
<tr>
<td colspan="2">Sub Threads</td>
</tr>
</thead>
<tbody>
{
container.mainthreads.map(function(subthread) {
return (<tr key={subthread.threadid}>
<td>{subthread.threadname}</td>
<td><button className="button alert">Delete</button></td>
</tr>)
})
}
<tr>
<td><input type="text"/></td>
<td><button className="button">Create</button></td>
</tr>
</tbody>
</table>
})}
</div>
)
}
});
ReactDOM.render(<AdminForumContainer/>, document.getElementById("AdminForumContainer"))
Upvotes: 0
Views: 224
Reputation: 24817
You're actually mutating your state, which is best avoided. That is, your for-loop is directly updating this.state.containers
, whereas state properties should be treated as read only, and only setState()
should update them.
So what's happening is you've already updated this.state.containers
by the time you call this.setState()
. I'm no expert on React internals, but what I think is happening is setState()
does a check to see if the state object property differs from the one you pass in; and here's the thing: it doesn't. The containers
array you passed in is the same as this.state.containers
. Therefore your component doesn't re-render.
My suggestion is to avoid mutating this.state.containers
. You can use Array#concat
to append the new data. Note that Array#concat
doesn't mutate the array, it creates a new one.
success: function(data) {
// add a loop here if you need to manipulate the data response in some way
this.setState({
containers: this.state.containers.concat(data)
});
}.bind(this)
UPDATE - 10 Dec 2015
A correction is in order.
Whilst I still think the above is good advice, I no longer believe it to be the source of your problem. Experimenting with React has shown my above conjecture about the nature of setState
to be false. React does not compare the old state to the new state before re-rendering (unless componentWillUpdate
is overridden specifically to do this). You can call this.setState()
with all or a subset of the existing state, an empty object or no argument at all, and it will still force a re-render. What it compares is not the old state to the new state, but the old rendered output to the new rendered output.
Upvotes: 0
Reputation: 7045
I would write it like that:
var containers = this.state.containers;
for (var i = 0; i < data.length; i++) {
containers.push(data[i]);
}
this.setState({containers: containers});
Does it work now ? What does show this.setState({containers: containers}, function() { console.log(this.state)); ?
Upvotes: 1