Fetching Data

Fetch with Loading and Error States

Example

A typical data-fetching effect. (Here the "server" is simulated with a promise.)

import { useState, useEffect } from 'react';

function fakeFetchUsers() {
  return new Promise((resolve) =>
    setTimeout(() => resolve([{ id: 1, name: 'Asha' }, { id: 2, name: 'Ravi' }]), 1000));
}

export default function App() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    fakeFetchUsers()
      .then(setUsers)
      .catch((e) => setError(e.message))
      .finally(() => setLoading(false));
  }, []);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error}</p>;
  return <ul>{users.map((u) => <li key={u.id}>{u.name}</li>)}</ul>;
}

Result

With fetch and async/await

useEffect(() => {
  const controller = new AbortController();
  async function load() {
    try {
      const res = await fetch('/api/users', { signal: controller.signal });
      if (!res.ok) throw new Error('HTTP ' + res.status);
      setUsers(await res.json());
    } catch (e) {
      if (e.name !== 'AbortError') setError(e.message);
    }
  }
  load();
  return () => controller.abort(); // cancel if the component unmounts
}, []);

Note: The effect function itself cannot be async; define an async function inside and call it.