Chapter 8 of 12
useEffect is the hook for running side effects — operations that interact with things outside React's render cycle, like fetching data, subscribing to events, or updating the document title.
ANALOGY
Side effects are anything that reaches outside React. React's job is rendering UI from data. Anything that goes outside that boundary — network requests, localStorage, timers, browser APIs, subscriptions — is a side effect. useEffect is where those operations live.
import { useState, useEffect } from "react";
function CourseList() {
const [courses, setCourses] = useState([]);
const [loading, setLoading] = useState(true);
// Runs after every render (no dependency array) — rarely what you want
useEffect(() => {
document.title = "Courses";
});
// Runs once on mount (empty dependency array)
useEffect(() => {
async function fetchCourses() {
try {
const res = await fetch("/api/courses");
const data = await res.json();
setCourses(data);
} finally {
setLoading(false);
}
}
fetchCourses();
}, []); // [] = run once when component mounts
// Runs when a specific value changes
useEffect(() => {
document.title = `Search: ${searchTerm}`;
}, [searchTerm]); // re-runs whenever searchTerm changes
if (loading) return <p>Loading...</p>;
return <ul>{courses.map(c => <li key={c.id}>{c.title}</li>)}</ul>;
}useEffect(() => {
// Set up a subscription or timer
const timer = setInterval(() => {
setCount(c => c + 1);
}, 1000);
// Return a cleanup function — runs before the next effect or on unmount
return () => {
clearInterval(timer); // prevents memory leak
};
}, []);
useEffect(() => {
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);