tl;dr: If you’re building a React app (or a framework) and want to achieve faster re-rendering on state updates, make sure to look at how Redux uses
unstable_batchedUpdates from the
Over the last few months we’ve been working on a state management framework for React. One of the things we were hoping to gain by using immer.js internally was improved performance due to more selective re-rendering, especially when updating state that affected a small number of components. Our initial benchmark was a visualization of a prime sieve up to 10,000:
Initially, we were sort-of right. The Prodo version slightly edged out Redux, beaten only by MobX. However, it took far longer to render the first few updates, where many cells were changed, than later ones, while Redux went through the whole sequence at a slower but more consistent pace.
To dig into why, we built an even more reduced benchmark, which just changes a random selection of a large grid. Some quick profiling with Firefox revealed we were spending most of our time in the React library, doing some sort of state update. The narrow tower on the left-most side shows the actual state update.
Meanwhile, Redux achieves the same thing with React doing much less work.
Internally, Prodo groups state updates into actions, with each action applied atomically. As actions can be asynchronous, it awaits the completion of each action and then triggers a state update. We found that removing the
await from synchronous actions allowed us to match Redux, but this did not allow React to re-render between an action and any child actions dispatched from it. StackOverflow quickly pointed out that within an event handler, state updates are batched into a single update.
However, we were still stuck as to how Redux managed to achieve its performance, and how we could match it without blocking re-renders. Redux had a
batch function, but it seemed to be a no-op . After some searching, we found that this secret is cleverly hidden in the Redux docs (of all places!) and wrapped our action commit logic in
unstable_batchedUpdates. After the fix, the Prodo version ran in 25s, beating both MobX at 55s and Redux at over 200s.
If you want to take advantage of batching in your own app, remember the following:
- Outside of an event handler, use
- If you call
setStatefrom within an event handler, updates are automatically batched