最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Race Condition in React setState - Stack Overflow

programmeradmin5浏览0评论

The Problem:

Multiple children of a ponent are having events triggered near simultaneously. Each of these events are handled by handleChange style functions which use React's immutability helpers to merge plex objects into the state of the controlling ponent, 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?

The Problem:

Multiple children of a ponent are having events triggered near simultaneously. Each of these events are handled by handleChange style functions which use React's immutability helpers to merge plex objects into the state of the controlling ponent, 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?

Share Improve this question edited Jun 28, 2016 at 1:16 Jake Haller-Roby asked Jun 28, 2016 at 1:09 Jake Haller-RobyJake Haller-Roby 6,4271 gold badge20 silver badges32 bronze badges 5
  • 1 Why are you setting state as an update of state? Just set state directly: this.setState({someKey: 'someVal'}); This solves your race issue, lowers the size of your package, and makes your code much simpler to read. The update addon is meant to be used on immutable objects. A ponent's state is not an immutable object - it is a normal JS object that may or may not contain immutable objects. – Matthew Herbst Commented Jun 28, 2016 at 2:06
  • React docs claim that the state should be treated as an immutable object. The merging is being done as a means to set fields dynamically from multiple sources, rather than having 50ish unique handleChange functions in one ponent to handle the large number of potential user inputs associated. – Jake Haller-Roby Commented Jun 28, 2016 at 17:59
  • The key word there is thought of. It is not however an immutable.js object. You should never modify this.state directly, only via this.setState() – Matthew Herbst Commented Jun 28, 2016 at 18:40
  • Ok, that's not really answering the question at all though... I know it's not actually immutable (my solution demonstrates that pretty clearly). The question is how to better address concurrent setState calls. – Jake Haller-Roby Commented Jun 30, 2016 at 17:29
  • 1 Except that isn't true... from the docs: "setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value. There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains." – Jake Haller-Roby Commented Jun 30, 2016 at 21:31
Add a ment  | 

1 Answer 1

Reset to default 16

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.

发布评论

评论列表(0)

  1. 暂无评论