NelsonLabs
React Fundamentals/useEffect — Side Effects

useEffect — Side Effects

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.

useEffect patterns
jsx
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>;
}
Cleanup — preventing memory leaks
jsx
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);
  };
}, []);