database-zapEngineering

State Management & Data Fetching

Server state vs client state, when to use Context, Zustand, or a query library, and avoiding global-state overreach.

1 item

Links1

01NotesNote

The key distinction: server state vs client state

Most "state management" pain comes from treating these as one problem. They aren't.

  • Server state is data that lives on the server and you cache locally — users, posts, anything from an API. It's asynchronous, shared, can go stale, and needs caching, revalidation, and loading/error handling. Don't manage this with useState + useEffect.
  • Client state is purely local UI state — a modal open/closed, a form's current value, the active tab. This belongs in useState/useReducer.

Server state → a query library (or the server)

  • In Next.js App Router, fetch on the server wherever possible (see the Next.js note) — that's the simplest "cache."
  • For client-side server state, use TanStack Query (React Query) or SWR. They give you caching, deduping, background revalidation, retries, and loading/error states out of the box. Re-implementing this with effects is how bugs breed.
  • Optimistic updates and cache invalidation after mutations are first-class — use them instead of manual refetch juggling.

Client state — escalate only as needed

  1. useState in the component. Most state is local; keep it there.
  2. Lift state to the nearest common parent when siblings need to share it.
  3. useContext for genuinely global, low-frequency values: theme, current user, locale. Caveat — every consumer re-renders when the context value changes, so don't put fast-changing state in Context.
  4. A store (Zustand / Jotai) when you have global, frequently-updated client state that Context would re-render too aggressively. Zustand is minimal and avoids the provider-nesting and boilerplate of older solutions.

The URL is state too

Filters, tabs, pagination, search queries — put them in the URL (search params). It's shareable, survives refresh, and works with the back button. Don't reinvent it in React state.

Rules of thumb

  • Don't reach for global state by default. Most state is local; global is the exception.
  • Never store server data in a global client store and try to keep it fresh by hand — that's exactly what a query library does correctly.
  • Derive, don't duplicate. If B can be computed from A, don't store B.