```
Escape hatch: if slots must be validated/reordered, accept named element props
(`header={
…
}`) — still elements, not strings-plus-config.
## State placement: the decision table
| The value is… | Home | Tool |
|---|---|---|
| Fetched from an API | Query cache | TanStack Query / SWR |
| Shareable via link (filters, tab, page) | URL | `useSearchParams` |
| Form input mid-edit | Local component | `useState` / form library |
| Theme, auth, locale (rarely changes, read widely) | Context | `createContext` |
| Everything else | Lowest component that uses it | `useState` |
## Controlled vs uncontrolled inputs
```tsx
// Controlled — other UI reacts per keystroke
const [query, setQuery] = useState('');
setQuery(e.target.value)} />
// Uncontrolled — value only needed on submit
```
## Custom hooks
```tsx
// Reusable logic = hook. Name starts with `use`, returns plain values.
function useDebouncedValue(value: T, delayMs = 300) {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const id = setTimeout(() => setDebounced(value), delayMs);
return () => clearTimeout(id);
}, [value, delayMs]);
return debounced;
}
const debouncedQuery = useDebouncedValue(query); // any component, same behavior
```
## Measured memoization
```tsx
// 1. Measure first: React DevTools Profiler → record → find components
// re-rendering with unchanged props AND non-trivial render cost.
// 2. Then, and only then:
const Row = React.memo(function Row({ item }: { item: Item }) { /* … */ });
// Parent must keep prop identities stable for memo to work:
const onSelect = useCallback((id: string) => setSelected(id), []);
// Comment the reason so the next reader knows it's load-bearing:
// memo: 5k rows re-rendered on every keystroke before this (Profiler 2026-08).
```
## Gotchas
- **`useEffect` for derivation** is the top React bug source — if the effect only
calls `setState` from other state/props, delete it and compute during render.
- **Index as `key`** breaks reordering/deletion — use a stable id; index is
acceptable only for static, never-reordered lists.
- **Object/array literals in JSX props** (`style={{…}}`, `options={[…]}`) defeat
`React.memo` children — hoist or memoize them only where memo is in play.
- **Context triggers re-render of every consumer** on any value change — split
contexts (state vs dispatch) or keep fast-changing values out of context.
- **`useCallback` without a memoized child** is pure overhead — it saves nothing.
- **Stale closure in intervals/subscriptions**: include deps or use a ref;
an empty dep array freezes captured state at mount time.
- **`'use client'` at the page root** opts the whole tree out of server
rendering — place it at the interactive leaf component instead.