In this article, we will look into the use of ReactDOM.unstable_batchedUpdates
within a test case, specifically in Zustand, a popular state management library for React. We'll also break down the test and explain how batched updates enhance performance in React by minimizing unnecessary re-renders.
Understanding the Test Case
Here is the test case we’ll be examining:
This test case is written to verify that batched updates can be applied when using Zustand with React’s rendering system.
Breaking Down the Test Case
1. Zustand Store Setup: The first step involves creating a Zustand store using the create
function:
const useBoundStore = create<CounterState>(
(set) => ({
count: 0,
inc: () => set((state) => ({ count: state.count + 1 })), }))
Here, the store maintains a simple state with a count
property initialized to 0 and an inc
function to increment the count. The set
function is Zustand’s way of updating the state, similar to setState
in React.
2. Counter Component: The Counter
component uses the useBoundStore
to retrieve the current count
and the inc
function:
const { count, inc } = useBoundStore()
This component subscribes to the store’s state, and any changes to count
will cause it to re-render with the new value.
3. Using ReactDOM.unstable_batchedUpdates
for Performance: Inside the useEffect
hook, the inc
function is called twice within a ReactDOM.unstable_batchedUpdates
block:
useEffect(() => {
ReactDOM.unstable_batchedUpdates(() => {
inc()
inc()
})
}, [inc])
This is where the magic happens. Normally, each call to inc()
would trigger a separate update, causing two renders. However, by wrapping these calls in unstable_batchedUpdates
, React is able to process them together in a single update, resulting in only one render. This optimizes performance by reducing the number of renders, which is especially useful in performance-critical applications.
4. Rendering the Component and Asserting the Result Finally, the component is rendered, and the test waits for the count
to reach 2:
const { findByText } = render(
<>
<Counter />
</>,
)
await findByText('count: 2')
This assertion ensures that after two increments, the count
is correctly updated and rendered as "count: 2".
What is ReactDOM.unstable_batchedUpdates
?
ReactDOM.unstable_batchedUpdates
is a method provided by React that allows multiple state updates to be processed in a single render cycle. By default, React batches updates triggered inside event handlers (for example, click event), meaning that if you update multiple states in response to a user interaction, React will render the component only once. However, outside of event handlers (like within setTimeout
or useEffect
), updates are not batched automatically.
But this has changed after React 18. Below are the screenshots picked from react.dev
Keep in mind, documentation suggests that updates inside of timeouts, promises, native event handlers or any other event will batch the same way as updates inside of React events. But in this Zustand’s test case, batch updates are applied inside useEffect`.
This is where unstable_batchedUpdates
becomes useful. It forces React to group multiple state updates into a single render, even outside of event-driven contexts, minimizing re-renders and improving performance.
Example:
Without unstable_batchedUpdates
:
inc() // triggers one render
inc() // triggers another render
With unstable_batchedUpdates
:
ReactDOM.unstable_batchedUpdates(() => {
inc() // triggers only one render for both updates
inc()
})
The method is labeled “unstable” because it’s not part of React’s official public API, but it is still widely used in the community for performance optimizations. It may become more stable or integrated as part of React’s new concurrent rendering capabilities in the future.
Fun fact: Zustand’s 4.5.5 release uses the version — 19.0.0-rc.0
Why Use ReactDOM.unstable_batchedUpdates
in Zustand?
Zustand is a lightweight state management library that works with React’s component lifecycle. Although Zustand efficiently handles state updates, React’s reactivity system will trigger renders every time the state changes. In scenarios where multiple state changes occur in a short period, using ReactDOM.unstable_batchedUpdates
can prevent multiple re-renders and batch the updates, allowing for a smoother, more efficient user experience.
In the test case provided, calling inc
twice within a batched update ensures that the count
only updates once, making it more efficient compared to running each update individually.
About us:
At Think Throo, we are on a mission to teach the advanced codebase architectural concepts used in open-source projects.
10x your coding skills by practising advanced architectural concepts in Next.js/React, learn the best practices and build production-grade projects.
We are open source — https://github.com/thinkthroo/thinkthroo (Do give us a star!)
Up skill your team with our advanced courses based on codebase architecture. Reach out to us at hello@thinkthroo.com to learn more!
References:
https://github.com/pmndrs/zustand/blob/v4.5.5/tests/basic.test.tsx#L175C7-L175C39
https://dev.to/jackbuchananconroy/react-18-what-s-changed-automatic-batching-13ec
https://react.dev/blog/2022/03/08/react-18-upgrade-guide#automatic-batching
https://github.com/pmndrs/zustand/blob/v4.5.5/package.json#L246C4-L247C32
Top comments (0)