Contents
In brief
A Save button flips to saved while the user keeps typing: the closure still holds the old text. A Dev.to post walks through useLatest from @reactuses/core — a ref updated in a layout effect so callbacks after await, timers, and subscriptions can read the last committed value.
What happened
Any callback that outlives its render (await, setTimeout, a listener, an SDK) sees the props/state of the render that created it. That is a normal closure; it is a bug only when you needed now and got then.
useLatest(value) returns a stable ref: .current updates in an isomorphic layout effect, not during render (concurrent React can discard a render). In the autosave example the closure holds the snapshot to send; the ref holds text after the API returns, so you can choose saved vs dirty.
Neighbours: useEvent / useEffectEvent wrap functions; useLatest wraps values. Typical spots: search races in handlers, a map/WebSocket with empty deps, a toast that pauses on hover.
Why it matters
Putting state in effect deps often means tearing down an expensive subscription. A ref keeps an honest empty deps array and fresh reads inside. Reading .current in JSX is wrong — the UI needs useState.
In practice
- After
await, compare the sent snapshot tolatest.current. - One-shot subscriptions should read through a ref, not deps that rebuild the client.
- Write the ref in a layout effect, not in the render body.
- Function for a child/effect →
useEvent; value for a callback →useLatest. - Don’t put the ref in deps “to react” — its identity is stable.
Takeaway
useLatest is a small pattern against stale closures: always current from callbacks, never triggers a re-render, never changes identity. For on-screen values, still use state.

