🟡 MediumClosure Traps🐛 Debug Challenge

Stale closure in React useEffect

Buggy Code — Can you spot the issue?

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

  useEffect(() => {
    const interval = setInterval(() => {
      console.log(count); // always logs 0!
      setCount(count + 1); // always sets to 1!
    }, 1000);
    return () => clearInterval(interval);
  }, []); // empty deps — closes over initial count=0

  return <div>{count}</div>;
}

Fixed Code

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

  useEffect(() => {
    const interval = setInterval(() => {
      // Fix 1: Use functional update (doesn't need count from closure)
      setCount(prev => prev + 1);
    }, 1000);
    return () => clearInterval(interval);
  }, []); // safe now — no stale closure

  // Fix 2 (if you need to read count): add to deps
  // useEffect(() => { ... }, [count]); — but this restarts interval each update

  return <div>{count}</div>;
}

Bug Explained

Bug: The effect captures count=0 at mount. The empty dep array means it never re-runs. The interval always uses the stale count=0.

Explanation: Functional updates (setCount(prev => prev + 1)) don't need to read the current state from closure — React provides the latest value as the argument.

Key Insight: Stale closures in React: use functional state updates or add the variable to useEffect dependencies.

More Closure Traps Debug Challenges

🟢 Easyvar in loop — buttons all say last value🔴 HardMemoization closure caching wrong scope🟡 MediumPrivate variable accidentally exposed🟡 MediumGenerator losing this context

Practice spotting bugs live →

38 debug challenges with AI hints

🐛 Try Debug Lab