Hooks
useState: batching & immutability
Functional updates, batching, lazy init, and derived-state pitfalls.
useState stores state that survives re-renders. The setter schedules a re-render; React batches multiple updates in the same event into one render for performance.
State updates are like mailing edits to a printer: batch several into one envelope, and always describe the change relative to the last copy, not the copy you remember.
Key concepts
1
Because updates are batched and the closure captures a stale value, use the functional form setCount(c => c + 1) when the next value depends on the previous. For expensive initial values, pass a lazy initializer function so it runs only once.
functional formlazy initializersetCount(c => c + 1)
2
State must be treated as immutable — spread into a new object/array; mutating in place skips the re-render because the reference is unchanged.
immutable
3
Pitfall — derived state: copying a prop into state and never syncing it. Prefer computing during render or useMemo. Interview angle: "why did two setCount(count+1) calls only increment once?" — both read the same stale count; the functional updater fixes it.
Pitfall — derived state:Interview angle:useMemosetCount(count+1)count
jsx
const [items, setItems] = useState(() => loadInitial()); // lazy init
function addTwice() {
setItems(list => [...list, 'a']); // functional — both apply
setItems(list => [...list, 'b']);
}
// Wrong: mutation is ignored by React
// items.push('x'); setItems(items);