Runnable lesson · press Try it on the examples to edit and run them here.
Rendering should only calculate JSX from props and state. But apps also need to do other things: set the page title, start a timer, talk to a server, subscribe to something. Those things are side effects, and useEffect is the hook for them.
The syntax
useEffect(() => {
// the effect: runs after React has updated the screen
return () => {
// optional cleanup: runs before the next effect and when the component is removed
};
}, [dependencies]);
Your first effect: the document title
function App() {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
After each render where count changed, React runs the effect and updates document.title. (In the playground it changes the title of the preview frame, which you cannot see. In a real project, watch the browser tab.)
When does it run?
- React renders your component and updates the screen.
- Then, after painting, React runs the effect.
So the effect always sees the committed result of that render. If you need to measure or change the layout before the browser paints, there is a sibling hook, useLayoutEffect; it is rarely needed.
The dependency array
The second argument controls when the effect runs:
| Dependency array | The effect runs |
|---|---|
Not given: useEffect(fn) | After every render |
Empty: useEffect(fn, []) | Once, after the first render (the “mount”) |
[a, b] | After the first render and after any render where a or b changed |
Effect Dependencies is entirely about this array.
function App() {
const [renders, setRenders] = React.useState(0);
const [ran, setRan] = React.useState(0);
React.useEffect(() => {
setRan((r) => r + 1);
}, []); // empty array: only after the first render
return (
<div>
<p>Render triggers: {renders}</p>
<p>Effect ran {ran} time(s)</p>
<button onClick={() => setRenders(renders + 1)}>Re-render</button>
</div>
);
}
What belongs in an effect
An effect is for synchronising your component with something outside React:
- the browser:
document.title, event listeners onwindow, timers, focus, scroll - a server: requests (though this has better options in frameworks and libraries)
- third-party widgets: maps, charts, video players
- a subscription: WebSocket, an event emitter
What does NOT belong in an effect
Many effects people write are not needed. Do not use an effect to:
- calculate a value from props or state: do it during render (Derived State)
- respond to a user event: do it in the event handler
- reset state when a prop changes: use a
key - chain state updates (an effect sets state, which triggers another effect…)
The lesson Do You Need an Effect? goes through these in detail. A good habit: before you write useEffect, ask “am I synchronising with an outside system?” If not, you probably do not need one.
Effects can run twice in development
In Strict Mode (which the Vite template uses), React mounts, unmounts and mounts your component again in development, to check that your effect cleans up properly. This only happens in development. You will see why in Cleanup Functions and Strict Mode.
Key points
useEffectruns code after rendering, to synchronise with something outside React.- The dependency array decides when it runs: every render, once, or when listed values change.
- Do not use effects for calculations or event responses.