useEffect
useEffect runs side effects after the DOM commits. Subscriptions, timers, manual data fetches, document title updates — anything that touches the world outside React goes in useEffect.
Effects + cleanup + deps
EXAMPLE
import { useEffect, useState } from 'react';
// 1) Run on every render — RARELY what you want
useEffect(() => {
console.log('every render');
});
// 2) Run once (on mount), cleanup on unmount — most common shape
useEffect(() => {
const id = setInterval(() => tick(), 1000);
return () => clearInterval(id); // cleanup
}, []);
// 3) Re-run when a dependency changes
useEffect(() => {
const ctrl = new AbortController();
fetch(`/api/posts/${postId}`, { signal: ctrl.signal })
.then((r) => r.json())
.then(setPost)
.catch((e) => { if (e.name !== 'AbortError') setError(e); });
return () => ctrl.abort();
}, [postId]);
// 4) Subscribe to an external store
useEffect(() => {
const unsub = store.subscribe(setSnapshot);
return unsub;
}, []);
// 5) Listen to a window event
useEffect(() => {
const onResize = () => setW(window.innerWidth);
window.addEventListener('resize', onResize);
return () => window.removeEventListener('resize', onResize);
}, []);
// 6) Update document.title — synced to a render value
useEffect(() => {
document.title = `${unread} unread`;
}, [unread]);
// 7) Async effect — declare an inner async function
useEffect(() => {
let active = true;
(async () => {
const data = await api.search(query);
if (active) setResults(data);
})();
return () => { active = false; };
}, [query]);
// 8) Common bugs + fixes
// 8a) Missing dep — stale closure
// ❌
useEffect(() => { fetchUser(userId); }, []); // re-uses initial userId
// ✅
useEffect(() => { fetchUser(userId); }, [userId]);
// 8b) Unstable object/function deps re-trigger every render
// ❌
useEffect(() => { ... }, [{ id }]);
// ✅
useEffect(() => { ... }, [id]); // primitive
const cb = useCallback(() => doThing(id), [id]);
useEffect(() => { cb(); }, [cb]);
// 9) Strict Mode runs effects twice in dev — use cleanup correctly and it's harmless
// 10) When NOT to use useEffect
// - Derived values → useMemo / compute during render
// - Event handlers → put the work in the handler, not in an effect
// - Data fetching → prefer a library (TanStack Query, SWR) — they handle race, cache, retry
Why it matters
useEffect is for “sync to the world.” If a value can be computed during render, do that — effects are how bugs sneak in.
Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.
Example
Example
useEffect(() => {
document.title = `Count: ${count}`;
return () => { /* cleanup */ };
}, [count]);
Try it Yourself »
Exercise
Side effects hook.
(() => { /* fetch */ }, []);
camelCase; nine chars.
Discussion
Loading…