Effects and Side Effects
Rendering should stay pure, no fetching, no timers, no subscriptions. The useEffect hook is where that kind of side effect belongs.
import { useState, useEffect } from 'react';
function CourseList() {
const [courses, setCourses] = useState([]);
useEffect(() => {
fetch('/api/courses')
.then((res) => res.json())
.then(setCourses);
}, []); // empty array: run once, when the component mounts
return (
<ul>
{courses.map((c) => <li key={c.id}>{c.title}</li>)}
</ul>
);
}
The dependency array
The second argument to useEffect controls when it re-runs:
| Dependency array | Behavior |
|---|---|
| omitted | Runs after every render |
[] | Runs once, after the first render |
[someValue] | Runs once after the first render, then again whenever someValue changes |
Cleaning up
If your effect subscribes to something (a timer, a WebSocket, an event listener), return a cleanup function from it, React calls it before the effect re-runs and when the component unmounts:
useEffect(() => {
const id = setInterval(() => console.log('tick'), 1000);
return () => clearInterval(id);
}, []);