Effects that set up subscriptions or async work must clean up on unmount or dependency change. This is one of the most common React interview deep dives.
Common Interview Questions
Q: When does cleanup run?
Answer: Before the effect re-runs (when deps change) and when the component unmounts. React calls the function returned from the effect.
Q: Abort fetch on unmount
Answer: Prevent setState on unmounted components and cancel in-flight requests when URL or component changes.
useEffect(() => {
const ctrl = new AbortController();
fetch(url, { signal: ctrl.signal })
.then((r) => r.json())
.then(setData)
.catch((e) => {
if (e.name !== "AbortError") setError(e);
});
return () => ctrl.abort();
}, [url]);
Q: Event listener cleanup?
Answer: Remove listeners with the same function reference. Stale closures happen if deps are wrong.
useEffect(() => {
const onResize = () => setWidth(window.innerWidth);
window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize);
}, []);
Q: Empty deps array meaning?
Answer: Effect runs once after mount; cleanup once on unmount. Only for mount-only setup — ESLint exhaustive-deps warns when values are missing.
Key Points for Interviews
-
Return cleanup from useEffect when needed
-
AbortController for fetch cancellation
-
Clear intervals/timeouts on unmount
-
Stale closure bugs = wrong dependency array
-
Strict Mode runs mount/unmount twice in dev to surface bugs
