Effects with useEffect
Synchronize with the outside world: fetching, timers and subscriptions.
What you will learn
- Write effects with correct dependencies
- Clean up effects
- Know when not to use an effect
Rendering should be a pure calculation: same props and state in, same JSX out. But apps also need to talk to things outside React: servers, timers, the browser title, event listeners. Effects are the escape hatch. useEffect runs your code after React has updated the screen.
import { useState, useEffect } from "react";
function Title({ name }) {
useEffect(() => {
document.title = `Hello ${name}`; // runs after render
}, [name]); // ...only when name changes
return <h1>{name}</h1>;
}The second argument is the dependency array:
[a, b]: run after the first render and wheneveraorbchanged.[]: run once after the first render.- omitted: run after every render (rarely what you want).
List every reactive value the effect reads (props, state, values derived from them). The lint rule react-hooks/exhaustive-deps checks this for you; treat its warnings seriously, because stale values are a top source of bugs.
Cleanup
An effect may return a function that React calls before the effect re-runs and when the component unmounts. Use it to stop timers, remove listeners and cancel subscriptions.
function Clock() {
const [now, setNow] = useState(new Date());
useEffect(() => {
const id = setInterval(() => setNow(new Date()), 1000);
return () => clearInterval(id); // cleanup
}, []);
return <p>{now.toLocaleTimeString()}</p>;
}In development, StrictMode runs effects twice (mount, unmount, mount) on purpose. If your effect breaks when run twice, it is missing a cleanup.
function User({ id }) {
const [user, setUser] = useState(null);
useEffect(() => {
let ignore = false; // avoid setting state from a stale request
fetch(`/api/users/${id}`)
.then((r) => r.json())
.then((data) => { if (!ignore) setUser(data); });
return () => { ignore = true; };
}, [id]);
return user ? <p>{user.name}</p> : <p>Loading...</p>;
}You might not need an effect
- Computing a value from props or state: do it during render, no effect needed.
- Responding to a click or submit: put the logic in the event handler.
- Resetting state when a prop changes: give the component a different
key.
An effect that sets state, with that same state in its dependency array, can loop forever. Also, an object or function created inline changes identity on every render, so listing it as a dependency re-runs the effect each time.
Try it yourself
Write a useWindowWidth-style effect: track window.innerWidth in state, update it on the resize event and remove the listener on cleanup.
Show solution
function Width() {
const [w, setW] = useState(window.innerWidth);
useEffect(() => {
const onResize = () => setW(window.innerWidth);
window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize);
}, []);
return <p>{w}px</p>;
}