Stable derived collections
A selector returns Object.keys(state), producing a new array while the keys remain unchanged.
Zustand · Const
useShallow wraps a Zustand selector with shallow-result memoization. It helps React components avoid re-rendering when selected object or array contents have not changed.
useShallow wraps a selector and returns a memoized selector whose selected result is compared shallowly. This is useful when a selector creates a new object or array reference, because unchanged selected values can retain the previous result and avoid an unnecessary component re-render.
const memoizedSelector = useShallow(selector)| Name | Type | Description |
|---|---|---|
selectorFn | (state: T) => U | A selector function that receives the current state and returns the data to select. |
A memoized selector function with type (state: T) => U.
import { create } from 'zustand'
import { useShallow } from 'zustand/react/shallow'
type Store = {
nuts: number
honey: number
addNuts: (n: number) => void
}
const useBearStore = create<Store>((set) => ({
nuts: 1,
honey: 2,
addNuts: (n) => set((state) => ({ nuts: state.nuts + n })),
}))
export function BearSummary() {
const summary = useBearStore(
useShallow((state) => ({ nuts: state.nuts, honey: state.honey })),
)
return <p>{summary.nuts} nuts, {summary.honey} honey</p>
}The component selects two fields through a shallow-memoized selector, so changes elsewhere in the store do not cause it to re-render.
A selector returns Object.keys(state), producing a new array while the keys remain unchanged.
A component needs several store fields grouped into one object.
A selector constructs a small object or array on each evaluation.
useShallow is a React Hook that accepts a selector function and returns a memoized selector. The returned selector uses shallow comparison to decide whether the selected result has meaningfully changed.
Follow-up: What alternative avoids creating a wrapper object? Subscribe to each selected value with its own selector, so no wrapper object needs to be recreated.
It is useful when a selector returns a newly allocated object or array, such as Object.keys(state) or an object containing several fields. Shallow comparison checks the selected entries rather than requiring the outer reference itself to remain identical.
Follow-up: When should it be avoided? If selecting individual fields is simple, separate selectors can avoid the wrapper allocation altogether.
The documented signature is useShallow<T, U = T>(selectorFn: (state: T) => U): (state: T) => U. selectorFn receives current state and returns the data to select; the result is another selector with the same input and output types.
Follow-up: What issue does this address in Zustand v5? A fresh selector result can fail Object.is equality on every update and may trigger an infinite update loop.
useStoreuseBearStoreselectorObject.isshallow comparison