Skip to content
D
Documentation

Select state efficiently

how-to
2 min readUpdated

Use useShallow when a selector builds an object or array from multiple state values and that result can remain shallowly equal across store updates. The component then keeps the same selected reference instead of re-rendering for an unchanged selection.

Select multiple values

The store hook created with create compares selector results with Object.is by default. Wrap a selector that creates a new object or array in useShallow when updates to other state leave its contents shallowly equal.

Install Zustand and the React packages used by this example:

bash
npm install zustand react react-dom
npm install --save-dev @types/react @types/react-dom

This React example selects two meal values in one object. The button changes a third value, so the meal summary stays selected as before and its render count does not increase; the separate meal display updates.

tsx
import { useRef } from 'react'
import { createRoot } from 'react-dom/client'
import { create } from 'zustand'
import { useShallow } from 'zustand/react/shallow'

const useMeals = create(() => ({
  papaBear: 'large porridge-pot',
  mamaBear: 'middle-size porridge pot',
  littleBear: 'A little, small, wee pot',
}))

function MealSummary() {
  const { papaBear, mamaBear } = useMeals(
    useShallow((state) => ({
      papaBear: state.papaBear,
      mamaBear: state.mamaBear,
    })),
  )
  const renders = useRef(0)
  renders.current += 1

  return (
    <section>
      <h2>Meal summary</h2>
      <p>{papaBear}; {mamaBear}</p>
      <p>Meal summary renders: {renders.current}</p>
    </section>
  )
}

function LittleBearMeal() {
  const meal = useMeals((state) => state.littleBear)

  return (
    <section>
      <p>Little Bear's meal: {meal}</p>
      <button
        onClick={() => useMeals.setState({ littleBear: 'blueberry pancakes' })}
      >
        Change Little Bear's meal
      </button>
    </section>
  )
}

function App() {
  return (
    <main>
      <MealSummary />
      <LittleBearMeal />
    </main>
  )
}

const container = document.getElementById('app')
if (!container) {
  throw new Error('Expected an element with id="app"')
}

createRoot(container).render(<App />)

Put a mount element in the page's HTML before loading this React entry point:

html
<div id="app"></div>
The initial view shows the meal summary and its render count, Little Bear's meal, and the change button.

Initially, the summary shows Papa Bear and Mama Bear's meals and a render count of 1. Click Change Little Bear's meal: Little Bear's displayed meal changes, while the summary and its count stay the same. The selector still creates a new object on each evaluation, but its selected properties have not changed, so useShallow returns the previous shallowly equal selection.

Choose a selector

Use useShallow when an object or array returned by a selector may be shallowly equal to the previous result. Its argument is a selector (state) => selectedValue; it returns a selector you pass to the store hook. It does not prevent a render when a selected property itself changes.

Selector resultExampleWhat the selection tracks
Object of picks({ papaBear: state.papaBear, mamaBear: state.mamaBear })The picked values; an update outside these values leaves the selection equal.
Array of picks[state.papaBear, state.mamaBear]The values in order; a changed value or order changes the selection.
Computed keysObject.keys(state)The resulting keys; changing a value without changing keys leaves the selection equal.

Shallow equality compares the top-level values, not nested contents. If a selected value changes, or the output is not shallowly equal, the component re-renders. A selector that returns a new reference without stabilizing it can cause unnecessary renders and, in v5, may cause an infinite loop. Use useShallow for shallowly equal outputs, or make the selector return a stable reference.

See it running

Try the Zustand live demo to see a running Zustand app.

Was this page helpful?