Async Data in React

Loading, Error and Race Conditions

Example

Switch users quickly: user 1 is slow, user 2 is fast. The ignore flag stops an old, slow response from overwriting the newer one.

import { useState, useEffect } from 'react';

const fetchUser = (id) =>
  new Promise((resolve) => setTimeout(() => resolve({ id, name: id === 1 ? 'Asha (slow)' : 'Ravi (fast)' }), id === 1 ? 1500 : 300));

export default function App() {
  const [id, setId] = useState(1);
  const [user, setUser] = useState(null);

  useEffect(() => {
    let ignore = false;
    setUser(null);
    fetchUser(id).then((u) => { if (!ignore) setUser(u); });
    return () => { ignore = true; };   // a newer request started
  }, [id]);

  return (
    <div>
      <button onClick={() => setId(1)}>User 1</button>
      <button onClick={() => setId(2)}>User 2</button>
      <p>{user ? user.name : 'Loading...'}</p>
    </div>
  );
}

Result