The simplest way to read state is to call the hook without any arguments:
import { create } from 'zustand'type BearStore = { bears: number fish: number}const useBearStore = create<BearStore>()((set) => ({ bears: 0, fish: 0,}))function BearCounter() { // Gets the entire state object const state = useBearStore() return <h1>{state.bears} bears and {state.fish} fish</h1>}
Reading the entire state object will cause your component to re-render on every state change, even if the component only uses a small part of the state.
function BearCounter() { // Only re-renders when bears changes const bears = useBearStore((state) => state.bears) return <h1>{bears} bears around here...</h1>}function FishCounter() { // Only re-renders when fish changes const fish = useBearStore((state) => state.fish) return <h1>{fish} fish in the pond</h1>}
Selectors use Object.is for equality checking by default. The component only re-renders if the selected value changes.
Inline selectors work fine but are recreated on every render:
function BearCounter() { // New function created on every render (still efficient) const bears = useBearStore((state) => state.bears) return <h1>{bears} bears</h1>}
Zustand handles inline selectors efficiently, but static selectors are marginally faster.
// Good: only re-renders when count changesconst count = useStore((state) => state.count)// Bad: re-renders on any state changeconst state = useStore()const count = state.count
2
Use primitive selectors when possible
// Good: primitive value comparisonconst userName = useStore((state) => state.user.name)// Less optimal: object comparisonconst user = useStore((state) => state.user)const userName = user.name
3
Use shallow equality for objects
import { useShallow } from 'zustand/react/shallow'// Prevents re-render if array contents are the sameconst todoIds = useStore( useShallow((state) => state.todos.map((t) => t.id)))