Blog
ReactDOM.unstable_batchedUpdates in Zustand testcase.

ReactDOM.unstable_batchedUpdates in Zustand testcase.

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:

  1. https://github.com/pmndrs/zustand/blob/v4.5.5/tests/basic.test.tsx#L175C7-L175C39

  2. https://dev.to/devmoustafa97/do-you-know-unstablebatchedupdates-in-react-enforce-batching-state-update-5cn2

  3. https://dev.to/jackbuchananconroy/react-18-what-s-changed-automatic-batching-13ec

  4. https://react.dev/blog/2022/03/08/react-18-upgrade-guide#automatic-batching

  5. https://github.com/pmndrs/zustand/blob/v4.5.5/package.json#L246C4-L247C32