Effects and Side Effects
Idioms for synchronizing with external systems after render - and avoiding effects for pure render work.
Busque em todas as páginas da documentação
Idioms for synchronizing with external systems after render - and avoiding effects for pure render work.
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
useEffect runs after the browser paints. Use it to talk to non-React systems: network, DOM APIs, third-party widgets.
useEffect(() => {
document.title = title;
}, [title]);Return a function to undo the subscription. React runs cleanup before the next effect and on unmount.
useEffect(() => {
const controller = new AbortController();
load(id, { signal: controller.signal });
return () => controller.abort();
}, [id]);[] means "run after mount, clean up on unmount." Only use when the effect truly never needs to re-subscribe.
useEffect(() => {
const theme = window.matchMedia("(prefers-color-scheme: dark)");
// ...
}, []);List every reactive value read inside the effect. When a dependency changes, React re-runs cleanup then the effect body.
useEffect(() => {
analytics.page(path);
}, [path]);Abort in-flight requests when id changes or the component unmounts so late responses cannot set state after unmount.
useEffect(() => {
const ac = new AbortController();
fetch(`/api/items/${id}`, { signal: ac.signal })
.then((r) => r.json())
.then(setItem)
.catch((e) => {
if (e.name !== "AbortError") setError(e);
});
return () => ac.abort();
}, [id]);Effects bridge React state to widgets that own their own state (maps, charts, video players). Keep the bridge thin and directional.
useEffect(() => {
map.setCenter(center);
}, [map, center]);Attach global listeners in an effect and always remove them in cleanup to avoid leaks and duplicate handlers.
useEffect(() => {
const onKey = (e: KeyboardEvent) => {
if (e.key === "Escape") onClose();
};
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, [onClose]);Store nothing extra if the interval only needs clear on unmount or dep change - pair setInterval with clearInterval.
useEffect(() => {
const id = window.setInterval(() => tick(), 1000);
return () => window.clearInterval(id);
}, [tick]);New object/array literals each render re-fire effects. Depend on primitive fields or memoize the value deliberately.
// Prefer:
useEffect(() => save({ x, y }), [x, y]);
// Avoid depending on `{ x, y }` created inline every renderMost "skip first" needs are better modeled as event handlers. If you must, track a mounted ref and gate the body carefully.
const isFirst = useRef(true);
useEffect(() => {
if (isFirst.current) {
isFirst.current = false;
return;
}
persist(filters);
}, [filters]);User-triggered work (submit, click) belongs in event handlers. Effects are for synchronization, not for "run this because the user did something" chains.
async function onSubmit(e: React.FormEvent) {
e.preventDefault();
await api.save(form);
}When debugging thrash, log which dependency changed between runs. Remove the noise once you fix the identity issue.
useEffect(() => {
console.debug("filters changed", filters);
}, [filters]);In development, React Strict Mode mounts, cleans up, and mounts again to surface missing cleanup. Write effects that are safe to start twice.
useEffect(() => {
const sub = store.subscribe(onChange);
return () => sub.unsubscribe();
}, [store, onChange]);For external stores, prefer useSyncExternalStore over ad-hoc useEffect + useState so concurrent rendering stays correct.
const snapshot = useSyncExternalStore(
store.subscribe,
store.getSnapshot,
store.getServerSnapshot,
);If state B is always derived from state A, compute B during render. Chained setState in effects causes extra renders and timing bugs.
const fullName = `${first} ${last}`; // not an effect that setFullNameStack versions: React 19 · TypeScript (strict) · Strict Mode friendly effects
Revisado por Chris St. John·Última atualização: 19 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥