Learn and Teach Coding
ReactBeginnerMay 20, 2024 · 9 min read

Understanding useEffect in React with Practical Examples

A clear, example-driven guide to the useEffect hook — dependencies, cleanup, data fetching, and the most common mistakes to avoid.

useEffect lets a component run side effects after it renders — things like fetching data, setting up subscriptions, or starting a timer. The hardest part isn't the syntax, it's understanding when the effect runs.

The dependency array

The second argument decides how often the effect runs:

// Runs after every render
useEffect(() => {
  console.log("rendered");
});

// Runs once, after the first render
useEffect(() => {
  console.log("mounted");
}, []);

// Runs whenever `userId` changes
useEffect(() => {
  loadUser(userId);
}, [userId]);

Leaving a value out of the dependency array doesn't make the bug go away — it just hides it. The effect will read a stale value from an earlier render.

Cleanup functions

If your effect starts something, it should also stop it. Return a function and React runs it before the next effect and when the component unmounts:

useEffect(() => {
  const id = setInterval(() => tick(), 1000);
  return () => clearInterval(id); // cleanup
}, []);

Fetching data safely

Use an AbortController so a request that's still in flight doesn't update state after the component has gone away:

useEffect(() => {
  const controller = new AbortController();
  fetch("/api/users", { signal: controller.signal })
    .then((res) => res.json())
    .then(setUsers)
    .catch((err) => {
      if (err.name !== "AbortError") setError(err);
    });
  return () => controller.abort();
}, []);

Here's that exact pattern, live — open the network tab and hit Refresh:

Users

Common mistakes

  • Missing dependencies — the effect captures stale props or state.
  • Objects/arrays in deps — they're recreated each render, so the effect runs every time. Memoize them with useMemo/useCallback.
  • Setting state synchronously in the effect body, causing extra renders. Set state in response to an event or after an awaited result instead.

Master the dependency array and cleanup, and useEffect stops being mysterious.

Related tutorials