Context and State Management
Put each piece of state where its owner and lifetime suggest: local first, the URL for shareable view state, a server cache for API data, and a store or context only for genuinely shared client state. Know exactly what makes each tool re-render.
Key points
- 1
Context passes a value down the tree; it does not manage state. Every consumer re-renders when the value changes by
Object.is, andmemocannot stop it. - 2
Keep provider values stable with
useMemo, provide stable setters, and split state from dispatch so components that only update don't re-render on every change. - 3
Store selectors (
useSelector, Zustand) re-render a component only when the selected value changes by reference, so selectors that build new arrays or objects need memoizing (createSelector) or shallow comparison (useShallow). - 4
Server state is remote, shared and asynchronous. TanStack Query caches it by key, deduplicates requests, refetches stale data in the background (staleTime) and drops unused data after gcTime (5 minutes by default).
- 5
Avoid duplicated and derived state: derive filtered lists in selectors, normalize entities by id, and treat the URL as the single source of truth for shareable filters.
- 6
On the server, module-level stores are shared by every request. Create stores and QueryClients per request inside a client provider.
- 7
React 19 lets you render
<Ctx value>as a provider and read context withuse(Ctx), which may be called conditionally.
Common traps
A provider passing
value={undefined}provides undefined; thecreateContextdefault applies only when there is no provider at all.Zustand 5 selectors that return a new object every call loop forever ("getSnapshot should be cached"); wrap them in
useShallow.In TanStack Query v5, a disabled query with no data is
pendingbut notloading(isLoading= isPending && isFetching).
Read the source
Test yourself on Context and State Management
Ten questions, with the answer and explanation after each one.