Skip to content
masterclassBuddyTech Buzz5 fresh blogs · 5:00 AM IST

Technology handbook

Zustand

Stores, selectors, actions, middleware, persistence, TypeScript, SSR and testing.

Topics
34
Sections
5

Hooks

useStore with a selector

Call the store hook with a selector so only that slice's changes re-render the component.

  • Select a primitive or stable reference from the store.
  • Unrelated state updates should not change the selected value.
  • Keep selector computation free of side effects.

useStoreWithEqualityFn

Supply a custom equality function where shallow comparison is not the right test.

  • Import the equality-aware hook from zustand/traditional.
  • Pass the vanilla store, selector and comparison function.
  • Ensure equality checks all fields the component actually uses.

createWithEqualityFn

Create a store whose hook accepts an equality function at each call site.

  • Use the traditional entry point for equality-aware bound hooks.
  • Choose a default comparison appropriate to the selected data.
  • Account for its use-sync-external-store peer dependency.

Binding a vanilla store

Read a framework-independent store from React without creating a second source of truth.

  • Create the vanilla store once for its intended scope.
  • Pass that instance to React's Zustand useStore hook.
  • Reuse its getState and actions outside React.

Context-scoped store hooks

Create the store per provider and expose it through one typed wrapper hook.

  • Keep a stable store instance inside the provider.
  • Throw a clear error when the wrapper lacks its provider.
  • Initialize each provider from its own input data.

Named slice hooks

Export a hook per slice so no call site can select the whole store by accident.

  • Expose focused hooks such as useCartCount or useCartItems.
  • Keep action selection separate from frequently changing values.
  • Let callers depend on a stable domain-facing API.

Hook-free access

getState and subscribe cover event handlers and non-React code, where no hook may run.

  • Use getState for a current imperative snapshot.
  • Use subscribe when outside code needs ongoing notifications.
  • Call the returned unsubscribe function during teardown.

Hooks in tests

Reset the store between tests and read state through the same hook the component uses.

  • Render hook consumers with the correct scoped store.
  • Wrap direct store updates in React act when needed.
  • Restore initial state so tests cannot inherit earlier changes.

Store fundamentals

create and bound hooks

Create a React-bound store and call the returned hook with a selector inside a component.

  • The create function returns a hook with store utilities attached.
  • Define actions alongside initial state in the initializer.
  • Invoke the hook only where React hooks are allowed.

createStore and vanilla stores

Use a framework-independent store with getState, setState and subscribe.

  • createStore returns a StoreApi without binding it to React.
  • Read snapshots with getState and update through actions.
  • Attach subscriptions only for their required lifetime.

State and actions

Keep update rules in named actions so callers express intent instead of editing store internals.

  • Name actions after domain operations such as addItem.
  • Keep related field updates together inside one action.
  • Expose only the state consumers need to read.

Functional updates

Read the latest previous state when computing a new value from the old one.

  • Use set with a callback when the next value depends on state.
  • Read previous values from that callback's state argument.
  • Avoid captured render snapshots for sequential updates.

Shallow merging and replacement

Understand the difference between merging fields and replacing the entire state, including actions.

  • Default set merges fields at the top level only.
  • Nested objects still require deliberate copying or replacement.
  • Replacement mode also removes omitted action functions.

Immutable nested updates

Copy changed nested objects or use a suitable Immer integration to notify subscribers correctly.

  • Copy each changed branch before assigning the new root field.
  • Retain references for nested data that did not change.
  • Do not mutate arrays obtained from getState.

Selectors & subscriptions

Atomic selectors

Subscribe to the smallest value needed by a consumer instead of the entire store.

  • Select a single field when only that field is displayed.
  • Prefer separate subscriptions for independent primitives.
  • Avoid selecting a whole collection for one item's count.

Stable selector results

Avoid returning a fresh object on every snapshot read when the values have not changed.

  • A new object or array can look changed on every read.
  • Reuse derived references when their inputs are unchanged.
  • Prefer primitives or shallow stabilization for simple selections.

useShallow

Reuse an appropriate object or array selection when shallow equality captures its semantics.

  • Wrap a selector with useShallow for shallow-equal outputs.
  • Use it for suitable flat arrays or object selections.
  • Nested mutation cannot be repaired by shallow comparison.

subscribeWithSelector

Observe selected state outside React and clean up subscriptions when their owner is removed.

  • Enable selector-aware subscribe through the middleware.
  • Provide a selector and listener for the external consumer.
  • Unsubscribe when the listener's owning feature is disposed.

Transient updates

Drive an external renderer from a subscription without forcing a React render for every update.

  • Subscribe directly for rapid canvas or animation updates.
  • Update the external target without storing duplicate React state.
  • Clean up the subscription when the target unmounts.

Derived state

Compute values from canonical state rather than storing redundant copies that can diverge.

  • Derive totals and filters from canonical store fields.
  • Memoize expensive derivations when input identities are stable.
  • Do not synchronize a second stored copy unnecessarily.

Middleware & architecture

persist middleware

Persist selected state with a deliberate storage key, version and hydration strategy.

  • Choose a stable storage name and explicit storage adapter.
  • Persist durable user preferences rather than temporary requests.
  • Decide how the UI behaves before rehydration completes.

partialize and migrations

Store only durable fields and transform older saved state when its shape changes.

  • Use partialize to exclude actions and transient fields.
  • Increment the persistence version when saved shape changes.
  • Test migration against real older stored payloads.

devtools middleware

Name actions and inspect transitions while excluding sensitive application data.

  • Give the store and actions meaningful debugging names.
  • Inspect before-and-after state for unexpected transitions.
  • Disable or sanitize sensitive data in shared diagnostics.

Immer middleware

Use draft-based updates within the middleware's contract and understand reference changes.

  • Draft mutation syntax is valid inside Immer-managed updates.
  • Return a replacement or mutate the draft, not both.
  • Use draftable structures and test nested reference changes.

Slices pattern

Compose domain slices into a store with explicit ownership and compatible middleware typing.

  • Define one slice per clearly owned domain.
  • Combine slice creators into the final store initializer.
  • Apply store-wide middleware at the combined boundary.

Context-scoped stores

Create separate store instances for independently mounted features or request-specific initial state.

  • Use independent instances for separate editors or feature mounts.
  • Supply the intended instance through React context.
  • Dispose subscriptions when that scope is removed.

Async actions and races

Represent pending and failed operations and prevent stale responses from overwriting newer state.

  • Store loading and error state with each asynchronous operation.
  • Compare request identities before accepting late results.
  • Abort obsolete requests when the underlying API supports it.

Resetting state

Restore initial data while preserving action functions and clearing persisted state when required.

  • Use the store's initial snapshot to restore defaults.
  • Preserve action functions when resetting data fields.
  • Clear persistent storage when reset means forgetting saved data.

Zustand integration

TypeScript store types

Type state, actions and middleware composition without unsafe casts hiding incompatible updates.

  • Declare separate state and action types for the store.
  • Preserve middleware type information through composition.
  • Infer selectors from the actual store state type.

Next.js and SSR

Scope stores correctly and ensure initial server and client snapshots agree.

  • Create per-request stores for server-derived user data.
  • Use matching initial snapshots during server render and hydration.
  • Access browser-only storage after the client is ready.

Hydration and persistence

Avoid displaying persisted browser data as though it existed in the initial server render.

  • Represent hydration completion explicitly for persisted views.
  • Avoid reading localStorage during server rendering.
  • Decide how persisted values merge with fresh server state.

Testing stores

Reset store state between tests and verify actions and selector-visible outcomes.

  • Test actions against representative initial and edge-case states.
  • Assert selected values and unchanged unrelated references.
  • Include persistence migration and race-condition scenarios.

Zustand versus server-state caches

Keep remote-data freshness and retry policies in a tool designed for server state when appropriate.

  • Use a server-state cache for freshness, retries and deduplication.
  • Keep local interaction state in the Zustand store.
  • Avoid maintaining a competing copy of query data.

Zustand versus Redux

Compare update discipline, middleware needs and team debugging requirements before choosing a store.

  • Compare lightweight store actions with Redux's event pipeline.
  • Consider auditability, middleware and team conventions.
  • Choose based on actual sharing and debugging requirements.

Sources

  • Zustand official documentation

Ask follow-up questions on any topic in your dashboard.

Start learning