The rules of React hooks are easy to recite and easy to get subtly wrong. When exactly does an effect re-run? Does typing in an unrelated input recompute my useMemo? Why didn't the screen update when I changed a ref? So I built a lab that logs every hook event the instant it happens.
▶ Live demo: https://hooks-lab-seven.vercel.app/
Source (React 19 + TS + Vite): https://github.com/dev48v/hooks-lab
Interact with a real component; a live timeline records every render, effect, cleanup, memo recompute, and ref mutation in order. Cause and effect sit right next to each other.
The things it makes obvious
useEffect runs after commit, and cleans up before the next run. Bump count and the timeline shows, in order:
RENDER render #3 — state changed: count
useMemo recomputed → expensive(1) = 2
cleanup useEffect[count] cleanup (was count = 0)
useEffect useEffect[count] ran (count = 1)
The cleanup-then-run pair is the thing people forget — and it's right there.
Dependencies gate effects and memos. Type into an unrelated name field:
RENDER render #2 — state changed: name
useMemo cached (count unchanged) → 0
useEffect useEffect[count] will be skipped (count unchanged)
The component re-rendered, but because count didn't change, useMemo returned its cached value and the [count] effect didn't run. Seeing "skipped" land in the log is what makes the dependency array click.
useRef mutates without rendering. Click "mutate ref" and the timeline logs ref.current = 1, but the number on screen stays stale — because nothing re-rendered. That staleness is the lesson.
setState with the same value bails out. setCount(count) produces a bail-out line and no render at all.
One implementation detail worth stealing
The component logs its own activity — but if those logs lived in React state, logging would trigger renders and pollute the very timeline you're watching. So events go into a tiny external store read with useSyncExternalStore. The panel updates; the measured component doesn't. (Also: no StrictMode, since it intentionally double-invokes renders/effects in dev and would double every line.)
It's npm install and nothing else — no UI library, just the bare mechanics.
If this made hooks click, a star helps others find it: https://github.com/dev48v/hooks-lab
Top comments (0)