Step 1 of 5 · Reading · ~2 min
Read
Hooks & State
The useEffect Hook
Render must be pure - it may run, be thrown away, and run again. Anything that reaches outside the component (network, timers, subscriptions, direct DOM work) goes in an effect, which runs after the commit.
The dependency array
[id]- re-run wheneveridchanges.[]- run once on mount; clean up on unmount.- omitted entirely - run after every render. Almost never what you want.
Deps are compared item by item with Object.is, so a fresh object or arrow
function in the list makes the effect re-run every time.
Cleanup
An effect may return a function. It runs before the next run of that same effect, and once more on unmount.
Unmount is the half people remember. The other half is the one that leaks:
change roomId from 1 to 2 with no cleanup and you now hold two open
sockets, both writing into the same component.
Storing an effect in a slot
That comparison is the exercise ahead: given the old deps and the new ones, decide whether this render runs the effect, skips it, or cleans up.
Traps
Leaving a value out of the deps freezes it at the render that created the
closure - the stale-closure bug again. Putting an unstable value in re-runs
the effect constantly. And two overlapping fetches can resolve out of
order, so a slow response for the previous id overwrites the fast one for
the current id; cleanup is where you cancel or invalidate it.
Newer APIs narrow the need for effects - useSyncExternalStore for
external state, Suspense and Server Components for data loading - but the
commit-then-effect ordering above is unchanged underneath all of them.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…