gravityplanx gravityplanx - 1 year ago 152
Javascript Question

Race Condition in React setState

The Problem:

Multiple children of a component are having events triggered near simultaneously. Each of these events are handled by

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}),
this.setState(React.addons.update(this.state, {$merge: new_value_object_B}),

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 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});

Which successfully avoids touching
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

The Question:

Is there a better way to solve this problem?

Answer Source

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;

    return newState 

Which allows you to access the current state (at time of execution) via the passed argument to that function.