Skip to content

Fetch Data in React

The browser's native fetch API loads data over HTTP. Combined with useEffect and useState, you can build loading/error/success UI — though TanStack Query simplifies this for most apps.

fetch + useEffect Pattern

Classic pattern: store data, loading, and error in state; fetch in useEffect when an ID dependency changes; abort on cleanup with AbortController.

Understand this pattern even if you adopt TanStack Query — it clarifies what libraries automate.

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

  useEffect(() => {
    const controller = new AbortController();
    setLoading(true);
    fetch(`/api/users/${userId}`, { signal: controller.signal })
      .then(r => { if (!r.ok) throw new Error(r.statusText); return r.json(); })
      .then(setData)
      .catch(e => { if (e.name !== 'AbortError') setError(e); })
      .finally(() => setLoading(false));
    return () => controller.abort();
  }, [userId]);

  if (loading) return <Spinner />;
  if (error) return <Error msg={error.message} />;
  return <Profile user={data} />;
}

AbortController prevents setState on unmounted components when userId changes quickly.

fetch Essentials

fetch(url, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(payload),
  signal: controller.signal,
})
  • fetch resolves on HTTP errors — check response.ok.
  • Always handle JSON parse errors and network failures.
  • Pass AbortSignal for cancellable requests.
  • Prefer TanStack Query for deduplication and cache.

fetch Quick Reference

Common fetch operations in React.

Task Code
GET JSON fetch(url).then(r => r.json())
POST JSON fetch(url, { method, body, headers })
Check status if (!res.ok) throw ...
Abort { signal: controller.signal }
FormData POST body: new FormData(form)
Headers auth Authorization: Bearer token

Avoiding Race Conditions

When dependencies change quickly, older responses may arrive after newer ones. AbortController or an ignore flag prevents stale data overwriting fresh results.

When to Migrate to TanStack Query

Repeated fetch effects across components, manual cache invalidation pain, and background refetch needs signal it's time for TanStack Query.

Common Mistakes

  • Not checking response.ok before parsing JSON.
  • Missing abort cleanup on dependency change.
  • Fetching in render body.
  • Duplicating identical fetches in sibling components.

Key Takeaways

  • fetch + useEffect + useState is the manual data loading pattern.
  • Use AbortController for cleanup and race safety.
  • Always handle loading, error, and success UI states.
  • TanStack Query replaces most manual fetch effects.

Pro Tip

Extract fetch logic to async functions — test them without React, use them inside effects or query hooks.