Hooks and effects
Clean up an Effect
Undo subscriptions, timers, connections, and stale asynchronous work before resynchronizing or removing the component.
An Effect that starts synchronization often needs to stop it. Return a cleanup function from useEffect:
useEffect(() => {
window.addEventListener('online', handleOnline)
return () => {
window.removeEventListener('online', handleOnline)
}
}, [])
React calls cleanup before this Effect runs again with changed dependencies. It also calls cleanup when the component leaves the tree.
Think in pairs:
- subscribe → unsubscribe
- connect → disconnect
- start timer → clear timer
- add listener → remove listener
- start request → ignore or abort stale result
Without cleanup, remounting the component can add another listener. One browser event then runs the handler several times. You might see duplicate console logs or duplicate network calls after navigating away and back.
The cleanup must refer to the same subscription or handler that setup created. An anonymous function passed separately to removeEventListener() is a different function and does not remove the listener.
Cleanup should undo the synchronization from that Effect, not reset unrelated application state. Do not clear form fields or wipe unrelated context in cleanup unless that is genuinely part of tearing down the external connection.
For async requests started inside an Effect, cleanup often sets a flag or uses an AbortController so a stale response cannot update state after the component unmounts.
Document which dependency changes should restart synchronization. An empty dependency array means setup runs once per mount; a missing array means setup runs after every render.
Mount and unmount the component three times, then dispatch one event. The handler should run once. Strict Mode can help reveal this leak during development by mounting components twice on purpose.
Lesson completed