useEffect Explained: Dependencies, Cleanup, and Pitfalls
May 19, 2026 · 9 min read
useEffect lets a component synchronise with something outside React — a network request, a subscription, a timer, the document title. It's one of the most useful hooks and one of the most misused. Here's a mental model that makes it predictable. (Its most common job — loading data — has its own guide: fetching data in React.)
What an effect is
An effect runs after React has rendered and committed to the DOM. Think of it as "after the screen updates, also do this." That's why effects are the right place for side effects, not the render body.
import { useEffect, useState } from 'react';
function Title() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = 'Clicked ' + count + ' times';
}, [count]);
return <button onClick={() => setCount(count + 1)}>+1</button>;
}The dependency array controls when it runs
- No array — the effect runs after every render.
- Empty array
[]— runs once, after the first render. - With values
[a, b]— runs after the first render and any render whereaorbchanged.
The rule of thumb: every value from the component that the effect uses should be in the array. Leaving things out causes stale values; this is the source of most "why isn't my effect updating?" questions.
Cleanup
If your effect starts something ongoing, return a cleanup function. React runs it before the effect runs again and when the component unmounts. This prevents leaks and duplicate subscriptions:
useEffect(() => {
const id = setInterval(tick, 1000);
return () => clearInterval(id); // cleanup
}, []);The classic infinite loop
Setting state inside an effect that also depends on that state re-triggers itself forever:
// runs, sets count, which re-runs the effect, which sets count...
useEffect(() => {
setCount(count + 1);
}, [count]);Fix it by removing the dependency, moving the logic into an event handler, or guarding the update. In general, if something can be computed during render or handled in an event, it probably doesn't belong in an effect at all.
Key takeaways
- Effects run after render — use them to sync with the outside world.
- List every reactive value the effect reads in the dependency array.
- Return a cleanup function for subscriptions, timers, and listeners.
- Prefer event handlers over effects when reacting to user actions.
Want the structured path? Explore the React roadmap or browse more articles.