Stale Closures in React: Why useState Gives You the Wrong Value

Hands-on practice for this lecture. Work through the exercises and quizzes to reinforce what you've learned.

1

Exercise 1 of 1

Stale Closure Quiz

Three closure scenarios in React hooks. Identify when a closure goes stale, what the interval actually logs, and when event handlers are NOT stale.

1
const [count, setCount] = useState(0);

useEffect(() => {
  const id = setInterval(() => {
    console.log(count); // what does this print?
  }, 1000);
  return () => clearInterval(id);
}, []); // empty deps

After clicking "+1" three times, what does the interval log?

2
const [count, setCount] = useState(0);

useEffect(() => {
  const id = setInterval(() => {
    setCount(c => c + 1); // functional update
  }, 1000);
  return () => clearInterval(id);
}, []);

Does this interval correctly increment count every second?

3
function Profile({ userId }) {
  const [user, setUser] = useState(null);

  const handleClick = () => {
    console.log('userId:', userId);
  };

  useEffect(() => {
    fetchUser(userId).then(setUser);
  }, [userId]);

  return <button onClick={handleClick}>{user?.name}</button>;
}

Is handleClick stale when userId changes?

0/3 answered

💡 Stale closures happen when a callback persists across renders (interval, subscription, timeout) and captures an old value. The fix: add the value to deps (so the effect re-runs) or use the functional updater form (so you never close over the value at all).

Practice: Stale Closures in React: Why useState Gives You the Wrong Value — Interactive Exercises | Durgesh Rai