Home / React / Lesson 6

useEffect — sync with the outside world

Intermediate 10 min read Lesson 6 of 11

Rendering should be a pure calculation: props + state → JSX. Anything that does something (network, setInterval, talking to document) belongs in useEffect.

import { useState, useEffect } from 'react';

function Title({ count }) {
  useEffect(() => {
    document.title = 'Clicks: ' + count;
  }, [count]);
  return <p>{count}</p>;
}

Cleanup

Return a function to undo the effect (clear timers, abort fetch, unsubscribe):

useEffect(() => {
  const id = setInterval(() => setNow(Date.now()), 1000);
  return () => clearInterval(id);
}, []);

Fetching

useEffect(() => {
  const ctrl = new AbortController();
  fetch('https://jsonplaceholder.typicode.com/todos/1', { signal: ctrl.signal })
    .then((r) => r.json())
    .then(setTodo)
    .catch((err) => {
      if (err.name !== 'AbortError') setError(err.message);
    });
  return () => ctrl.abort();
}, []);

In React 18+ Strict Mode (on in Vite’s default main.jsx), React mounts, unmounts, and remounts once in development to catch missing cleanups. You may see two network calls in dev. That is expected; production mounts once.