React
Purpose
Write React where state lives in one place, effects are rare, and re-renders are understood rather than suppressed with memoization applied at random.
When to Use
- Building or reviewing React components.
- Diagnosing unnecessary re-renders or stale state.
- Deciding where state belongs: local, lifted, context, or server.
- Removing
useEffect calls that should not exist.
Capabilities
- Component decomposition and state colocation.
- Hook correctness: dependencies, cleanup, and the rules that are not optional.
- State management selection:
useState, useReducer, context, external store, server state.
- Data fetching with TanStack Query or the framework's own loader.
- Render profiling and targeted memoization.
Inputs
- The component tree and where data enters it.
- The interaction and its performance characteristics, if performance is the concern.
- React version — the correct answer changed with 18 and again with 19.
Outputs
- Components with a single source of truth for each piece of state.
- Effects only where genuinely synchronizing with an external system.
- Memoization applied where a profile shows it is needed.
Workflow
- Locate the state — Put it as close to where it is used as possible. Lift only when two siblings need it. Reach for context only when prop drilling exceeds about three levels.
- Separate server state from client state — Data from an API is cache, not state. It has staleness, refetching, and error semantics that
useState does not model. Use a query library.
- Delete unnecessary effects — An effect that computes a value from props belongs in render. An effect that resets state on a prop change belongs in a
key. Most useEffect calls in a typical codebase should not exist.
- Profile before memoizing — React DevTools Profiler shows what actually re-renders and why.
useMemo on a cheap computation costs more than it saves.
- Make the dependencies honest — Never silence the exhaustive-deps lint rule. If the array is wrong, the bug is a stale closure, and it will be intermittent.
Best Practices
- Derived state is a bug. If a value can be computed from props or other state, compute it during render.
useEffect is for synchronizing with something outside React: a subscription, a DOM API, a timer. It is not for reacting to state changes.
- Every effect that subscribes must return a cleanup function. Missing cleanup is the standard cause of memory leaks and duplicate listeners.
- Do not put a non-stable key on a list. Index keys break every time the list is reordered or filtered.
- Context re-renders every consumer when its value changes. Split contexts by update frequency, or use an external store with selectors.
- Lift state up only as far as needed. State in the root component re-renders the tree.
Examples
An effect that should not exist:
// Wrong: derived state, an extra render, and a chance to be out of sync.
function Cart({ items }) {
const [total, setTotal] = useState(0);
useEffect(() => {
setTotal(items.reduce((sum, i) => sum + i.price * i.qty, 0));
}, [items]);
return <Total value={total} />;
}
// Right: compute it during render. It is always correct, by construction.
function Cart({ items }) {
const total = items.reduce((sum, i) => sum + i.price * i.qty, 0);
return <Total value={total} />;
}
Server state belongs in a query, not in useState plus useEffect:
function OrderList({ status }) {
const { data, isPending, error } = useQuery({
queryKey: ["orders", status],
queryFn: ({ signal }) => fetchOrders(status, { signal }),
staleTime: 30_000,
});
if (isPending) return <Skeleton />;
if (error) return <ErrorState error={error} => refetch()} />;
return <List items={data} />;
}
The manual version needs loading state, error state, cancellation on unmount, a race-condition guard when status changes mid-flight, and a cache. That is what the library is.
Notes
- The React Compiler (React 19) auto-memoizes and removes most hand-written
useMemo and useCallback. Do not spend effort on memoization you are about to delete.
- Strict Mode in development intentionally double-invokes effects to surface missing cleanup. An effect that breaks under Strict Mode is broken in production too — it just fails less often.
key on a component is the idiomatic way to reset its state when an identity changes. It is far cleaner than an effect that resets fields.
1---2name: react3description: Use when writing or reviewing React. Covers component and state design, hook correctness, memoization that is actually needed, data fetching, and the render behavior behind most React performance problems.4---56# React78## Purpose910Write React where state lives in one place, effects are rare, and re-renders are understood rather than suppressed with memoization applied at random.1112## When to Use1314- Building or reviewing React components.15- Diagnosing unnecessary re-renders or stale state.16- Deciding where state belongs: local, lifted, context, or server.17- Removing `useEffect` calls that should not exist.1819## Capabilities2021- Component decomposition and state colocation.22- Hook correctness: dependencies, cleanup, and the rules that are not optional.23- State management selection: `useState`, `useReducer`, context, external store, server state.24- Data fetching with TanStack Query or the framework's own loader.25- Render profiling and targeted memoization.2627## Inputs2829- The component tree and where data enters it.30- The interaction and its performance characteristics, if performance is the concern.31- React version — the correct answer changed with 18 and again with 19.3233## Outputs3435- Components with a single source of truth for each piece of state.36- Effects only where genuinely synchronizing with an external system.37- Memoization applied where a profile shows it is needed.3839## Workflow40411. **Locate the state** — Put it as close to where it is used as possible. Lift only when two siblings need it. Reach for context only when prop drilling exceeds about three levels.422. **Separate server state from client state** — Data from an API is cache, not state. It has staleness, refetching, and error semantics that `useState` does not model. Use a query library.433. **Delete unnecessary effects** — An effect that computes a value from props belongs in render. An effect that resets state on a prop change belongs in a `key`. Most `useEffect` calls in a typical codebase should not exist.444. **Profile before memoizing** — React DevTools Profiler shows what actually re-renders and why. `useMemo` on a cheap computation costs more than it saves.455. **Make the dependencies honest** — Never silence the exhaustive-deps lint rule. If the array is wrong, the bug is a stale closure, and it will be intermittent.4647## Best Practices4849- Derived state is a bug. If a value can be computed from props or other state, compute it during render.50- `useEffect` is for synchronizing with something outside React: a subscription, a DOM API, a timer. It is not for reacting to state changes.51- Every effect that subscribes must return a cleanup function. Missing cleanup is the standard cause of memory leaks and duplicate listeners.52- Do not put a non-stable key on a list. Index keys break every time the list is reordered or filtered.53- Context re-renders every consumer when its value changes. Split contexts by update frequency, or use an external store with selectors.54- Lift state up only as far as needed. State in the root component re-renders the tree.5556## Examples5758**An effect that should not exist:**5960```jsx61// Wrong: derived state, an extra render, and a chance to be out of sync.62function Cart({ items }) {63 const [total, setTotal] = useState(0);64 useEffect(() => {65 setTotal(items.reduce((sum, i) => sum + i.price * i.qty, 0));66 }, [items]);67 return <Total value={total} />;68}6970// Right: compute it during render. It is always correct, by construction.71function Cart({ items }) {72 const total = items.reduce((sum, i) => sum + i.price * i.qty, 0);73 return <Total value={total} />;74}75```7677**Server state belongs in a query, not in `useState` plus `useEffect`:**7879```jsx80function OrderList({ status }) {81 const { data, isPending, error } = useQuery({82 queryKey: ["orders", status],83 queryFn: ({ signal }) => fetchOrders(status, { signal }),84 staleTime: 30_000,85 });8687 if (isPending) return <Skeleton />;88 if (error) return <ErrorState error={error} onRetry={() => refetch()} />;89 return <List items={data} />;90}91```9293The manual version needs loading state, error state, cancellation on unmount, a race-condition guard when `status` changes mid-flight, and a cache. That is what the library is.9495## Notes9697- The React Compiler (React 19) auto-memoizes and removes most hand-written `useMemo` and `useCallback`. Do not spend effort on memoization you are about to delete.98- Strict Mode in development intentionally double-invokes effects to surface missing cleanup. An effect that breaks under Strict Mode is broken in production too — it just fails less often.99- `key` on a component is the idiomatic way to reset its state when an identity changes. It is far cleaner than an effect that resets fields.