React State Management
Priority: P0 (CRITICAL)
Implementation Guidelines
- Selection: Zustand for small-medium apps (minimal boilerplate, no Providers). Redux Toolkit (RTK) for large apps needing time-travel debugging or complex middleware.
- Server Data: Use React Query or SWR for server state. Never sync server data into
useStatemanually. Let cache source of truth. - Form/Mutation State (React 19): Use
useActionStatefor a form's pending/result/error triad instead of three separateuseStatecalls; useuseOptimisticto show an optimistic value while a mutation is in flight, reverting on error automatically. - Context API: Use for low-frequency data like theme, auth, locale, or DI. Not for high-frequency updates (causes global re-renders). Split Context between State and Dispatch to optimize.
- Global Updates: Use Zustand, Jotai, or Redux for frequent/complex updates across app.
- Local:
useStatefor simple UI toggles.useReducerfor complex state machines. - Derived: Compute at render time (
const fullName = ...). NouseEffectto sync state. - URL: Store filter/sort params in URL Search Params (Single Source of Truth).
- Immutability: Never mutate. Use spread or Immer. Use
useMemoon context value to prevent unnecessary re-renders (primitive performance tuning belongs inhooksskill).
Boundary note:
hooksskill covers primitive API usage (useMemo,useCallbackrules). This skill covers architectural state decisions — which tool to use for which state scope.
function LikeButton({ postId, initialLiked }: { postId: string; initialLiked: boolean }) {
const [optimisticLiked, setOptimisticLiked] = useOptimistic(initialLiked);
return (
<button
() => {
setOptimisticLiked(!optimisticLiked);
await toggleLike(postId);
}}
>
{optimisticLiked ? 'Liked' : 'Like'}
</button>
);
}
Verify
- Form pending/error/result state uses
useActionState, not threeuseStatecalls. - In-flight mutation UI uses
useOptimisticrather than a manual "isSaving" flag. - Server data lives in React Query/SWR cache, never duplicated into
useState.
Reference & Examples
For Zustand, Redux Toolkit, and TanStack Query patterns: See references/REFERENCE.md.
Anti-Patterns
- No Context for High-Freq: Use Zustand/Redux for state that changes frequently.
- No State Sync: Compute derived values during render; avoid
useEffectto sync state. - No Server Cache as UI State: React Query/SWR for server data; don't duplicate into
useState. - No Manual Optimistic Flags: Use
useOptimisticinstead of a hand-rolled temporary state + rollback.