The Problem:
Multiple children of a component are having events triggered near simultaneously. Each of these events are handled by handleChange
style functions which use React's immutability helpers to merge complex objects into the state of the controlling component, via something similar to;
this.setState(React.addons.update(this.state, {$merge: new_value_object}));
This works fine when the events trigger independently, but when multiple events cause updates to the state in this way, each is individually merging from the old version of the state. I.e. (psuedo-code, not intended to execute).
function logState() { console.log(this.state) }
logState(); // {foo: '', bar: ''}
var next_value_object_A = {foo: '??'}
var next_value_object_B = {bar: '!!'}
this.setState(React.addons.update(this.state, {$merge: new_value_object_A}),
logState);
this.setState(React.addons.update(this.state, {$merge: new_value_object_B}),
logState);
Would produce;
{foo: '??', bar: ''}
{foo: '', bar: '!!'}
Terrible solution that I don't want to use:
The following seems to work, but also seems to be a major anti-pattern;
setSynchronousState: function(nextState){
this.state = React.addons.update(this.state, {$merge: nextState});
this.setState(this.state);
}
This relies on modifying the State directly. I don't see any immediate problems in running this code, and it does solve the problem at hand, but I have to imagine that I'm inheriting some massive technical debt with this solution.
A slightly better version of this solution is;
getInitialState: function(){
this._synchronous_state = //Something
return this._synchronous_state;
},
_synchronous_state: {},
setSynchronousState: function(nextState){
this._synchronous_state = React.addons.update(this._synchronous_state, {$merge: nextState});
this.setState(this._synchronous_state);
}
Which successfully avoids touching this.state
directly, though now we have the issue of conflicting information being passed around the application. Each other function now needs to be congnizant of whether it is accessing this.state
or this._synchronous_state
.
The Question:
Is there a better way to solve this problem?
Answering my own question in case anyone else ever sees this;
this.setState
can take in a function as it's argument, rather than an object, which looks like this;
this.setState(function(state){
...
return newState
});
Which allows you to access the current state (at time of execution) via the passed argument to that function.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With