How To Solve The React Hook Closure Issue?

The reason the alert shows the outdated value of count is because the callback passed to setTimeout is referencing an outdated value of count captured by the closure. This is usually referred to as a stale-closure.

On the initial render, the anonymous function passed as a callback to setTimeout captures the value of count as 0, and when the button show alert gets clicked the callback gets queued but with the outdated value of count.

In the case above the easiest solution to show the updated value of count in the alert message and fix the stale-closure issue will be to use a ref.

function App() {
  const [count, setCount] = useState(0);

  const latestValue = useRef(count);

  const handleAlertClick = () => {
    setTimeout(() => {
      alert(`count is: ${latestValue.current}`);
    }, 3000);
  };

  return (
    <div>
      <p>You clicked {count} times</p>
      <button
        onClick={() => {
          setCount(prev => {
            latestValue.current = prev + 1;
            return prev + 1;
          });
        }}
      >
        Click me
      </button>
      <button onClick={handleAlertClick}>Show alert</button>
    </div>
  );
}

Working demo in codesandbox

Hooks rely heavily on closures to work, so it very likely that you may bump into problems regarding stale-closures. Here is a nice article on how stale-closures create issues when using react-hooks and demonstrates how to fix some the issue in some situations.

Leave a Comment