Prompt
Write a custom useDebounce hook that delays updating a value until a specified delay (ms) has passed since the last change.
Solution
Custom hooks are the payoff of the hooks redesign: stateful logic as a plain composable function. Before hooks, "debounce this value" meant a HOC or render prop wrapping your component; now it's four lines that drop into any component with no wrapper cost — which is exactly why hooks exist.
The mechanism is an elegant inversion: the hook doesn't fight re-renders, it uses them. Every keystroke re-renders the component with a new value; the effect's dependency array [value, delay] sees the change, runs the cleanup from the previous effect first — cancelling the pending timer — then schedules a fresh one. Only when the value survives delay milliseconds untouched does the timeout fire and update debouncedValue. The cleanup function is the entire algorithm; without it you'd stack timers and fire once per keystroke, just late.
Note the two-state design: the raw value stays responsive (the input never lags), while consumers read the debounced one. That split — urgent UI state versus derived, rate-limited state — is the same shape useDeferredValue formalizes.
Red flag: Debouncing the handler with a bare setTimeout and no cleanup. It leaks timers, fires with stale closures, and keeps running after unmount. The effect-plus-cleanup version is the answer that shows you understand the effect lifecycle.
Say it: "The cleanup function is the debounce — each value change cancels the previous timer via effect cleanup, so only a value that survives the full delay ever commits."