Skip to main content
This guide is only relevant if you’re using React versions before React 18. React 18 and later handle batching automatically.

The Problem

In React versions before 18, setState is handled synchronously when called outside a React event handler. This means updating Zustand state outside an event handler will force React to update components synchronously, which can lead to the zombie-child effect.

What is the Zombie-Child Effect?

The zombie-child effect occurs when:
  1. A parent component re-renders
  2. A child component reads stale data before it can re-render
  3. The child makes decisions based on outdated state

The Solution: unstable_batchedUpdates

Wrap your state updates in unstable_batchedUpdates to batch updates and prevent this issue:
import { unstable_batchedUpdates } from 'react-dom' // or 'react-native'

const useFishStore = create((set) => ({
  fishes: 0,
  increaseFishes: () => set((prev) => ({ fishes: prev.fishes + 1 })),
}))

const nonReactCallback = () => {
  unstable_batchedUpdates(() => {
    useFishStore.getState().increaseFishes()
  })
}

Common Scenarios

import { unstable_batchedUpdates } from 'react-dom'

// Outside React event handler
setTimeout(() => {
  unstable_batchedUpdates(() => {
    useFishStore.getState().increaseFishes()
    useBearStore.getState().increaseBears()
  })
}, 1000)

When Do You Need This?

Need unstable_batchedUpdates

  • setTimeout/setInterval callbacks
  • WebSocket message handlers
  • Fetch/Promise callbacks
  • Third-party library callbacks
  • Browser event listeners (addEventListener)

Don't Need It

  • onClick, onChange, etc. (React synthetic events)
  • useEffect hooks
  • React 18+ applications (automatic batching)
  • Synchronous updates in render

React Native

For React Native applications, import from react-native instead:
import { unstable_batchedUpdates } from 'react-native'

const nonReactCallback = () => {
  unstable_batchedUpdates(() => {
    useFishStore.getState().increaseFishes()
  })
}

Upgrading to React 18

If you upgrade to React 18 or later, you can remove all unstable_batchedUpdates calls. React 18 automatically batches all state updates, regardless of where they occur.

Migration Example

import { unstable_batchedUpdates } from 'react-dom'

setTimeout(() => {
  unstable_batchedUpdates(() => {
    useStore.getState().updateValue()
  })
}, 1000)

Additional Resources

GitHub Issue #302

Read more details about this issue and the discussion around batched updates in Zustand
Consider upgrading to React 18 or later to benefit from automatic batching and avoid this complexity altogether.

Build docs developers (and LLMs) love