React: Calling multiple setState's inside a timeout callback causes multiple rerenders

Created on 12 Feb 2019  Â·  2Comments  Â·  Source: facebook/react

Do you want to request a feature or report a bug?
Report a bug

What is the current behavior?
Calling setState inside a timeout callback causes multiple rerenders.

Here is a demo showing this:
https://codesandbox.io/s/w710184o8w

On component mount, I'm making it fire a 1000ms timeout and calling multiple setState. I have a counter to count the amount of rerenders. The button also has another callback, which fires onClick, also calling the same setState. The set timeout callback causes 4 rerenders, while the onClick callback only causes a single rerender, as expected.

This is probably not specific to setTimeout, but this is the simplest way I found to reproduce this problem.

What is the expected behavior?
I expected the setTimeout callback to also cause a single rerender only.

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
React 16.8+

Most helpful comment

Note this issue isn't specific to Hooks — multiple calls to setState on a class inside a timeout would work the same way.

In general, we recommend using a single state variable (or a reducer) for closely related things that tend to change together.

All 2 comments

This is expected behavior. setState runs synchronously for updates that occur when React isn't currently reconciling. setTimeout pushes the setState call to the top of the call stack, after React is already finished rendering, so it runs synchronously.

React has a synthetic event system, so event handlers are called as part of the rendering process. Since React owns that step it can reliably batch updates together.

See this comment for a detailed overview of batching.

You can force React to batch updates together using ReactDOM.unstable_BatchedUpdates

ReactDOM.unstable_batchedUpdates(() => {
  // React will attempt to batch all these updates
  setState1(false)
  setState2(true)
  // ...
})

Keep in mind that the API is marked as unstable_ so be cautious about relying on it.

Note this issue isn't specific to Hooks — multiple calls to setState on a class inside a timeout would work the same way.

In general, we recommend using a single state variable (or a reducer) for closely related things that tend to change together.

Was this page helpful?
0 / 5 - 0 ratings