Overview
This card is the lookup table; patterns and trade-offs are in react-hooks and react-performance. Call hooks only at the top level of a component or custom hook, never in conditions, loops, or nested functions, and name custom hooks useX. The eslint-plugin-react-hooks package enforces both rules.
Core hooks
Use the simplest hook that fits; most state needs only useState.
| Hook | Use for | Notes |
|---|---|---|
useState(init) | Local state | useState(() => expensive()) runs the initializer once; setX(prev => prev + 1) when the next value depends on the last. |
useReducer(reducer, init) | State with several sub-values or transitions | Dispatch typed actions; the reducer must be pure. |
useRef(init) | DOM nodes and mutable values that do not render | Changing .current does not re-render. |
useContext(Ctx) | Read shared values (theme, auth) | Every consumer re-renders when the value changes. |
useEffect(fn, deps) | Sync with an external system | Return a cleanup; runs after paint. |
useLayoutEffect(fn, deps) | Measure layout before paint | Blocks painting; use sparingly. |
useMemo(fn, deps), useCallback(fn, deps) | Cache a value / a function identity | Needed mainly for memo children and effect deps. |
Effects
Use effects only to sync with external systems, and always clean up.
| Deps | Runs |
|---|---|
| none | After every render (rarely right). |
[] | On mount, and unmount for the cleanup. |
[a, b] | When a or b changes (compared with Object.is). |
useEffect(() => {
const ctrl = new AbortController();
fetch(`/api/items?q=${q}`, { signal: ctrl.signal })
.then((r) => r.json())
.then(setItems)
.catch((e) => { if (e.name !== "AbortError") setError(e); });
return () => ctrl.abort(); // cleanup runs before the next effect and on unmount
}, [q]);Never make the effect function itself async; call an inner async function.
React 19 hooks and APIs
Use these for async actions, optimistic UI, and effect callbacks.
| API | Use for |
|---|---|
use(promiseOrContext) | Read a promise (with Suspense) or context; may be called conditionally. |
useActionState(action, init) | Form actions: returns [state, formAction, isPending]. |
useOptimistic(value) | Show an optimistic value while an async action runs. |
useTransition() | [isPending, startTransition]; mark updates as non-urgent. |
useDeferredValue(value) | Let a slow subtree lag behind a fast input. |
useEffectEvent(fn) | Read the latest props or state inside an effect without making them dependencies (React 19.2+); call it only from effects. |
useId(), useSyncExternalStore(sub, get) | Stable ids for accessibility; subscribe to external stores. |
React Compiler 1.0 (stable since October 2025) memoizes components automatically when enabled, so hand-written useMemo and useCallback are then mostly unnecessary.
Gotchas
Avoid these; each causes stale values, extra renders, or loops.
| Gotcha | Fix |
|---|---|
| An object, array, or function created in render as a dependency re-runs the effect every render. | Move it outside, memoize it, or depend on primitives. |
| A callback captures stale values when its deps are missing. | List every value it reads, use a functional update, or use useEffectEvent. |
Reading or writing ref.current during render breaks purity. | Do it in effects and event handlers. |
| Strict Mode runs effects twice in development. | Make cleanup undo setup exactly. |
| Deriving state from props in an effect adds a render and a bug source. | Compute the value during render. |
A setState in an effect that depends on that state loops. | Check what the effect reads and writes. |
| A context value rebuilt every render re-renders all consumers. | Split contexts or memoize the value. |