Keep Async React Callbacks Fresh: A Deep Dive into the useLatest Hook

Laptop by a flowing stream in a forest

Why Async Callbacks Often Miss the Latest State

In modern React applications, asynchronous operations such as API calls, timers, or event listeners frequently run after a component has re‑rendered. When a function captures state at the moment of its creation, it forms a closure that may reference an outdated value once the async work completes. This “stale closure” problem can cause UI inconsistencies, such as an autosave button indicating a successful save while the user continues typing.

Illustrative Example: An Autosave Button with Stale State

function Editor({ docId }) {
  const [text, setText] = useState("");
  const [status, setStatus] = useState("idle");

  async function save() {
    setStatus("saving");
    await api.save(docId, text); // captures "text" from the time save() was called
    setStatus("saved"); // ⚠️ user may have typed during await
  }

  return (
    
      

Close filters
Products Search