React · Lesson 5 of 7
Effects with useEffect
Synchronize with the outside world: fetching, timers and subscriptions.
- Intermediate
- 16 min read
- 3 objectives
Before this lessonLesson 4: Events, Lists and Forms
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.
Anatomy
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.
Fetching data
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.
// Write your solution here
