React9h estimated

Hooks & Closures

Difficulty
Importance

The problem

Components often need to run side effects (fetching data, subscribing to events) synchronized with rendering, and need to share stateful logic between components without the deep wrapper nesting older patterns required — hooks let function components have state and lifecycle behavior directly.

Why now

Functions and scope already covered closures as functions that remember their creation environment; hooks are built entirely on that mechanism — each render creates a new closure over that render's specific state and props, which explains both hooks' power and their most common pitfall (stale closures).

Mental model

Every render of a component creates a fresh closure, and any hook callback created during that render (an event handler, a useEffect callback) closes over that specific render's values — not the 'current' ones a mental model borrowed from regular variables would expect. A 'stale closure' bug is exactly this: a callback from an old render still referencing that old render's now-outdated state, because it was never re-created.

Requires

Used in

Projects

  • Write a useEffect that reads a piece of state without listing it in the dependency array, observe the stale-closure bug directly, then fix it
  • Extract a repeated piece of stateful logic (e.g. window size tracking) from two components into one shared custom hook

Examples

  • useEffect(() => { console.log(count) }, []) always logs the count from the very first render — the closure was only created once, with count frozen at its initial value
  • A custom hook useWindowSize() lets any component subscribe to window resize events with a single line, sharing logic instead of duplicating it

Resources

Mastery checklist