I\'m running into some problems with React.js and the state not being immediately set when calling setState(). I\'m not sure if there are better ways to approach this, or if it
Yes, setState is asynchronous, so this.state
won't be updated immediately. Here are the unit tests for batching, which might explain some of the details.
In the example above, alarmSet
is data computed from the alarmTime
and elapsedTime
state. Generally speaking, computed data shouldn't be stored in the state of the object, instead it should be computed as-needed as part of the render method. There is a section What Shouldn’t Go in State? at the bottom of the Interactivity and Dynamic UIs docs which gives examples of things like this which shouldn't go in state, and the What Components Should Have State? section explains some of the reasons why this might be a good idea.
As Douglas stated, it's generally not a good idea to keep computed state in this.state
, but instead to recompute it each time in the component's render
function, since the state will have been updated by that point.
However this won't work for me, as my component actually has its own update loop which needs to check and possibly update its state at every tick. Since we cannot count on this.state
to have been updated at every tick, I created a workaround that wraps React.createClass
and adds it's own internal state tracking. (Requires jQuery for $.extend) (Fiddle: http://jsfiddle.net/kb3gN/4448/)
var Utils = new function() {
this.createClass = function(object) {
return React.createClass(
$.extend(
object,
{
_state: object.getInitialState ? object.getInitialState() : {},
_setState: function(newState) {
$.extend(this._state, newState);
this.setState(newState);
}
}
)
);
}
}
For any components where you need up-to-date state outside of the render function, just replace the call to React.createClass
with Utils.createClass
.
You'll also have to change all this.setState
calls with this._setState
and this.state
calls with this._state
.
One last consequence of doing this is that you'll lose the auto-generated displayName
property in your component. This is due to the jsx transformer replacing
var anotherComponent = React.createClass({
with
var anotherComponent = React.createClass({displayName: 'anotherComponent'
.
To get around this, you'll just have to manually add in the displayName property to your objects.
Hope this helps
Unsure if this was the case when question was asked, though now the second parameter of this.setState(stateChangeObject, callback)
takes an optional callback function, so can do this:
setAlarmTime: function(time) {
this.setState({ alarmTime: time }, this.checkAlarm);
},
checkAlarm: function() {
this.setState({
alarmSet: this.state.alarmTime > 0 && this.state.elapsedTime < this.state.alarmTime
});
}, ...