Reputation: 453
Is there any way to make sure render
is called after setState
actually updates the state? From my knowledge, setState
is asynchronous. I have tried putting render
as the callback to setState
, but it still renders before the state is updated.
HTML code:
<div id="root"></div>
React code:
class Table extends React.Component {
constructor(props) {
super(props);
this.state = {
squares: []
};
this.create = this.create.bind(this);
}
create() {
console.log('squares before: ' + this.state.squares);
var a = [];
for (var i = 0; i < 16; i++) {
a.push(undefined);
}
i = Math.floor(Math.random() * 8);
a[i] = 2;
i = Math.floor(Math.random() * 8) + 8;
a[i] = 2;
console.log('new squares: ' + squares);
console.log('non-updated squares: ' + this.state.squares);
this.setState({
squares: a
}, console.log('updated squares: ' + this.state.squares);
}
componentWillMount() {
this.create();
}
render() {
console.log('rendering');
return (<div>
<table className="table">
<tbody>
<tr>
<td className="td"><Number number={this.state.squares[0]}/></td>
<td className="td"><Number number={this.state.squares[1]}/></td>
<td className="td"><Number number={this.state.squares[2]}/></td>
<td className="td"><Number number={this.state.squares[3]}/></td>
</tr>
<tr>
<td className="td"><Number number={this.state.squares[4]}/></td>
<td className="td"><Number number={this.state.squares[5]}/></td>
<td className="td"><Number number={this.state.squares[6]}/></td>
<td className="td"><Number number={this.state.squares[7]}/></td>
</tr>
<tr>
<td className="td"><Number number={this.state.squares[8]}/></td>
<td className="td"><Number number={this.state.squares[9]}/></td>
<td className="td"><Number number={this.state.squares[10]}/></td>
<td className="td"><Number number={this.state.squares[11]}/></td>
</tr>
<tr>
<td className="td"><Number number={this.state.squares[12]}/></td>
<td className="td"><Number number={this.state.squares[13]}/></td>
<td className="td"><Number number={this.state.squares[14]}/></td>
<td className="td"><Number number={this.state.squares[15]}/></td>
</tr>
</tbody>
</table>
</div>);
}
}
class Game extends React.Component {
newGame() {
this.refs.table.create();
}
render() {
return (<div>
<Table ref="table"/>
<button id="newGame" onClick={this.newGame.bind(this)}>New Game</button>
</div>);
}
}
Basically, what happens in this is that 'rendering' gets logged first and then 'updated squares: ...' like this:
squares before: [undefined, 2, undefined, undefined, undefined, 2]
new squares: [undefined, undefined, 2, undefined, 2, undefined]
non-updated squares: [undefined, 2, undefined, undefined, undefined, 2]
rendering
updated squares: [undefined, undefined, 2, undefined, 2, undefined]
I tried making the callback to setState
render
, but the second render would render after the first render but before setState
updated state
:
this.setState({
squares: a
}, this.render);
It logged:
squares before: [undefined, 2, undefined, undefined, undefined, 2]
new squares: [undefined, undefined, 2, undefined, 2, undefined]
non-updated squares: [undefined, 2, undefined, undefined, undefined, 2]
rendering
rendering
Any help would be greatly appreciated. Thanks in advance!
Upvotes: 2
Views: 1059
Reputation: 169
use shouldComponentUpdate(object nextProps, object nextState), for render, each time it returns true, render will happen
Upvotes: 0
Reputation: 282160
A synchronous
setState
in componentWillMount
doesn't trigger a re-render instead reflects the change in the initial render itself.The callback to setState however is called after the render method.
If you log the state squares
inside the render method, it will log the correct result in the initial render itself.
P.S. componentWillMount
is deprecated in the latest versions and its better to have your current logic in constructor instead of componentWillMount
Upvotes: 2