Community JavaScript Snippet
useLatest Ref: The Anti-Stale-Closure Pattern
The five-line hook I reach for whenever an effect, a setTimeout, or an external subscription needs to call back into the latest value of a prop or state without re-binding.
useLatest Ref: The Anti-Stale-Closure Pattern
The five-line hook I reach for whenever an effect, a setTimeout, or an external subscription needs to call back into the latest value of a prop or state without re-binding.
By @petrawilson
December 24, 2025
·
Updated May 18, 2026
851 views
20
4.4 (8)
useLatest is the smallest custom hook I keep around: three lines of body, one assignment per render. The bug it solves is the most common cause of "why is my React app stale": a setTimeout or websocket handler is registered once, and the function it calls captured count from the render that registered it, not the current one. Running the assignment outside useEffect is intentional: effects fire after commit, but the render itself is what the next callback should see. The shim above does not re-render across calls, so accordion 1 fakes two renders to print the contrast: stale closure sees 0, useLatest sees the most recent value.
This is the production pattern that justifies useLatest. A setInterval that should always call the latest onTick cannot include onTick in its dep array (the timer would reset every render, drifting the cadence). Stashing the callback in a ref and reading callbackRef.current() from inside the interval gives you both: a stable timer and an always-fresh handler. Outside React I cannot drive a real interval through three component renders, so the demo manually swaps cbRef.current over time to simulate the same effect. In a real component I have used this for charts, websocket sends, and analytics throttles; in each case the alternative was a re-binding useEffect that broke the timing.
