Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ReactJS concurrent SetState race condition

I have a component structure like this

<A>
    <B>
        <C/>
        <C/>
    </B>
    <D>
       <E/>
       <E/>
    </D>
</A>

Idea is that actions on components in block E are processed by a function of component A to state of A and than passed down to B and C as props. I know, that better way was to use Flux, pubsub-js or other Store-message system, but hope if someone can explain why correct to the best of my understanding solution doesn't work.

Calling this function of component A simalteneously from multiple instances of component E leads to race condition with only one change in state (instead of each function call providing a change)

updateState(value,index){
   this.setState(update(this.state, {
        a: {
          [index]: {
            b: {
             $set: value
            }
          }
        }
    })
);
}

Function update here comes from

import update from 'react/lib/update';

Bad solution that goes against ReactJS reccomended practices, but works well:

updateState(value,index){
   this.state.a[index].b=value;
   this.forceUpdate();
);
}

My question is:

Is it a bug, that multiple simalteneous setState invokes a race condition, or I'm doing something wrong without understnding it?

like image 874
akaprog Avatar asked May 20 '15 01:05

akaprog


People also ask

Can we use multiple setState in React?

Because of all the work actually involved when the setState() function is called, React may batch multiple setState() calls into a single update to improve performance. Thanks to this batching process, this. state might be updated asynchronously which means you cannot always rely on the current this.

How do you avoid race condition in React?

We can fix this race condition by “canceling” the setData call for any clicks that aren't most recent. We do this by creating a boolean variable scoped within the useEffect hook and returning a clean-up function from the useEffect hook that sets this boolean “canceled” variable to true .

Can setState be synchronized?

Setting State Is Asynchronous React sets its state asynchronously because doing otherwise can result in an expensive operation. Making it synchronous might leave the browser unresponsive. Asynchronous setState calls are batched to provide a better user experience and performance.

Can you call setState twice?

React will call that function with state and props, and use the result for next state. It is safe to call setState with a function multiple times. Updates will be queued and later executed in the order they were called.


2 Answers

You probably want to pass a function to setState which should remove such race conditions. Something like:

this.setState(prevState => {
  return {
    someProp: prevState.someProp + 1,
  };
});

Even if two different parts of your application do this at the same time, both functions will still be called and someProp will be incremented twice.

If you were to do this instead: this.setState({someProp: this.state.someProp + 1}) it could only be incremented once because this.state.someProp isn't updated directly after calling setState. But when passing a function to setState you get the previous state as an argument, which lets you avoid data races.

like image 57
Anders Ekdahl Avatar answered Jan 01 '23 08:01

Anders Ekdahl


According to React documentation, as mentioned in the first answer, you can pass a function to ensure the atomicity's operation. The thing is that you should not modify the state but return the new value inside the passed function:

setState(function(previousState, currentProps) {
  return {myInteger: previousState.myInteger + 1};
});

https://facebook.github.io/react/docs/component-api.html

like image 21
Borja Martín Avatar answered Jan 01 '23 09:01

Borja Martín