Fullstack CourseLearn by building
Back to week 4

Topic

Challenge: stale state and Effects

Definition

A stale closure occurs when asynchronous or deferred code reads state captured by an earlier render instead of the current value required by the behavior.

In simpler words

A timer or Effect can remember an old value if its dependencies or update form are wrong.

This challenge reinforces that Effects synchronize external systems; they are not a workaround for state flow.

After this you can

  • Spot a stale state bug
  • Select a dependency-safe update

Debug the render boundary

Definition

React closures capture values from the render that created them, so state updates based on a previous value should use the functional updater form.

In simpler words

Let React supply the latest value instead of trusting an old captured value.

Inspect the Effect dependencies and whether the callback needs the previous state.

Keep cleanup paired with subscriptions and timers.

Keep in mind

  • If a value is derived during render, do not mirror it into Effect-managed state.

Challenge lab

Bug report

The elapsed counter sometimes stops at 1 after a route transition.

What is broken

An interval Effect calls setElapsed(elapsed + 1) with an empty dependency list.

Broken snippet

useEffect(() => {
  const id = setInterval(() => {
    setElapsed(elapsed + 1); // stale closure
  }, 1000);
  return () => clearInterval(id);
}, []); // elapsed missing — and restarting on elapsed is worse

Pass criteria

  • Counter continues from the current value
  • Interval is cleaned up on unmount
  • No unnecessary Effect restart

Checking your session…

Choose the correct fix

Test

Check your understanding

At least 10 questions — mix of concept, syntax, practical, and logic. Score ≥80% (enforced by the API) to save progress.

Checking your session…

15 questions · concept 5 · syntax 3 · practical 4 · logic 3

1. What does a React challenge usually test?
Concept
2. If state updates don’t show?
Syntax
3. List challenge with edits?
Practical
4. Derived UI in challenges?
Logic
5. Forms in challenges?
Concept
6. Component boundaries?
Practical
7. Passing callbacks?
Syntax
8. When stuck on a challenge?
Logic
9. Purity reminder?
Concept
10. Success criteria mindset?
Practical
11. Starting at 0, what is count after one click?
Conceptadvanced
function onClick() {
  setCount(count + 1);
  setCount(count + 1);
}
12. Why might the list not re-render?
Syntaxintermediate
function add(item) {
  items.push(item);
  setItems(items);
}
13. Why does this delete immediately on render?
Practicalintermediate
<button onClick={deleteItem(id)}>Delete</button>
14. What is the cleaner fix in this challenge?
Logicadvanced
const [items, setItems] = useState([]);
const [count, setCount] = useState(0);
useEffect(() => setCount(items.length), [items]);
15. Why is key={i} risky here?
Conceptintermediate
{rows.map((r, i) => <Row key={i} row={r} />)}
// rows can be sorted; Row has local state

Checking your session…