Reactintermediate 9 min
Run side effects such as timers, subscriptions and data loading, and clean them up properly.
What a side effect is, how useEffect runs code after rendering, how the dependency array controls when it runs, and how to clean up.
Rendering should only calculate what to show. But apps also need to do other things: load data, start timers, listen to the window, update the page title. useEffect is the place for those.
A side effect is anything a component does besides returning JSX: fetching data, setting a timer, changing the document title.
The dependency array decides when the effect runs:
[] (empty): once, after the first render.[count]: after the first render and whenever count changes.Cleanup undoes the effect: clear a timer, remove a listener, cancel a subscription. Without it, effects pile up and leak.
Rule: every value from the component that the effect uses should be in the dependency array.
running. When it is true, it starts a timer that adds one to seconds every second.running changes (and when the component is removed), so only one timer is ever active.setSeconds((s) => s + 1) uses the functional form so the timer always sees the latest value.seconds changes.running becomes false, the cleanup stops the timer and the new effect run returns early.Watch the counter tick in the preview, press Pause and Resume, then change the interval to 500 milliseconds.
useEffect runs side effects after render.Next: useRef, for values that should not cause re-renders.
useEffect(() => {
// runs after the component renders
return () => {
// optional cleanup: runs before the next effect and when unmounting
};
}, [dependencies]);function Timer() {
const [seconds, setSeconds] = useState(0);
const [running, setRunning] = useState(true);
useEffect(() => {
if (!running) return;
const id = setInterval(() => setSeconds((s) => s + 1), 1000);
return () => clearInterval(id);
}, [running]);
useEffect(() => {
document.title = "Seconds: " + seconds;
}, [seconds]);
return (
<div style={{ fontFamily: "system-ui" }}>
<h2>{seconds} seconds</h2>
<button onClick={() => setRunning(!running)}>{running ? "Pause" : "Resume"}</button>
<button onClick={() => setSeconds(0)}>Reset</button>
</div>
);
}
function App() {
return <Timer />;
}