DevPath · Learn to code ESPTEN

useEffect, useRef and effects

Fetching data with useEffect

Requesting data from an API

Getting data from a server is a classic side effect: it happens outside React, it's asynchronous and shouldn't happen during render. That's why the usual pattern is to fire the request inside a useEffect and store the result in state.

Since the network takes time and may fail, it's modeled with three states:

function User({ id }) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    let active = true;            // to ignore stale responses
    setLoading(true);
    setError(null);

    fetch("/api/users/" + id)
      .then((res) => {
        if (!res.ok) throw new Error("Response " + res.status);
        return res.json();
      })
      .then((json) => {
        if (active) { setData(json); setLoading(false); }
      })
      .catch((err) => {
        if (active) { setError(err.message); setLoading(false); }
      });

    return () => { active = false; }; // cleanup: avoids "race conditions"
  }, [id]);

  if (loading) return <p>Loading…</p>;
  if (error)   return <p>Error: {error}</p>;
  return <h2>{data.name}</h2>;
}

Key points of this pattern:

  1. While the data arrives a loading state is shown (Loading…). The user never sees a broken screen while waiting for the network.
  2. The request goes in useEffect with [id] as a dependency: if the id changes, the correct user is loaded again.
  3. The cleanup (active = false) discards responses that arrive late, when the component already requested something else or unmounted (avoids race conditions).

In real apps this logic is usually delegated to libraries like React Query or SWR, which handle caching, retries and states for you. But they all rely on this same loading / error / data pattern.

Put this into practice

DevPath is a hands-on course: you read the theory here; in the app you put it into practice with exercises that really run, offline.

Start free in the app →
← useEffect: side effectsuseRef: DOM and mutable values →