DevAcademy
LearnReactuseEffect & Side Effects
IntermediateReact

useEffect & Side Effects

Learn how to synchronize a component with external systems — like APIs, timers, and subscriptions — using useEffect.

Reading Time

20 min

Lesson

Lesson 13 of 42

What is a Side Effect?

Rendering should be a pure calculation of JSX from props and state. A side effect is anything that reaches outside that calculation — fetching data, subscribing to an event, manually touching the DOM, or starting a timer.

A Basic useEffect

import { useEffect, useState } from "react";

function DocumentTitle({ title }) {
  useEffect(() => {
    document.title = title;
  }, [title]);

  return <h1>{title}</h1>;
}

The Dependency Array

The second argument to useEffect tells React when to re-run the effect. It re-runs whenever any value in that array changes between renders.

Dependency Array Behavior

Dependency ArrayWhen the Effect Runs
Omitted entirelyAfter every single render
[] (empty array)Only once, after the first render
[a, b]After the first render, and again whenever a or b changes

Fetching Data on Mount

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetch(`/api/users/${userId}`)
      .then((res) => res.json())
      .then((data) => setUser(data));
  }, [userId]); // re-fetches whenever userId changes

  if (!user) return <p>Loading...</p>;
  return <h2>{user.name}</h2>;
}

Cleanup Functions

If an effect returns a function, React calls it before the effect runs again, and once more when the component unmounts — the right place to cancel subscriptions, clear timers, or remove event listeners to avoid memory leaks.

Cleaning Up an Interval

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const id = setInterval(() => {
      setSeconds((s) => s + 1);
    }, 1000);

    return () => clearInterval(id); // cleanup: runs on unmount
  }, []);

  return <p>{seconds}s elapsed</p>;
}

Missing Dependencies Cause Stale Values

If an effect uses a prop or state value but omits it from the dependency array, the effect can keep using an outdated ("stale") value from an earlier render. Most editors with the eslint-plugin-react-hooks rule will warn about this automatically.

Not Every Side Effect Needs useEffect

Event handlers (like a click handler making an API call) don’t need useEffect — they already run in response to a specific user action. useEffect is specifically for synchronizing with something external whenever the component renders with new values.

Best Practice

Always include every value from component scope that your effect uses in the dependency array. If that causes the effect to re-run too often, that’s usually a sign the effect (or the surrounding state) needs restructuring, not that the dependency should be omitted.

Interview Questions

Quick Quiz

1. What does an empty dependency array [] mean for useEffect?

2. What does a function returned from useEffect do?

3. Why is a missing dependency in the dependency array risky?