5 min read
0%

AbortController in React

Back to Blog
AbortController in React

AbortController in React

AbortController lets you cancel in-flight fetch requests. In React, this is essential for cleaning up effects that start async operations.

The Problem Without Cleanup

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetch(`/api/users/${userId}`)
      .then(r => r.json())
      .then(setUser); // may run after component unmounts or userId changes
  }, [userId]);
}

If userId changes or the component unmounts while the fetch is in flight, setUser runs on stale or dead state.

AbortController Solution

useEffect(() => {
  const controller = new AbortController();

  fetch(`/api/users/${userId}`, { signal: controller.signal })
    .then(r => r.json())
    .then(setUser)
    .catch(err => {
      if (err.name === 'AbortError') return; // expected — ignore
      setError(err);
    });

  return () => controller.abort(); // cancel on cleanup
}, [userId]);

When userId changes, the old effect cleans up (controller.abort()), cancelling the previous fetch before the new one starts.

Multiple Requests

One controller can abort multiple requests:

useEffect(() => {
  const controller = new AbortController();
  const { signal } = controller;

  Promise.all([
    fetch('/api/user', { signal }).then(r => r.json()),
    fetch('/api/posts', { signal }).then(r => r.json()),
  ]).then(([user, posts]) => {
    setUser(user);
    setPosts(posts);
  }).catch(err => {
    if (err.name !== 'AbortError') setError(err);
  });

  return () => controller.abort();
}, []);

Timeout with AbortSignal

// Manual timeout
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);
fetch(url, { signal: controller.signal }).finally(() => clearTimeout(timeoutId));

// Newer API
fetch(url, { signal: AbortSignal.timeout(5000) });

Custom Hook

function useFetch(url) {
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const controller = new AbortController();
    setLoading(true);

    fetch(url, { signal: controller.signal })
      .then(r => r.json())
      .then(setData)
      .catch(err => { if (err.name !== 'AbortError') setError(err); })
      .finally(() => setLoading(false));

    return () => controller.abort();
  }, [url]);

  return { data, error, loading };
}

StrictMode and Double-Fetch

React StrictMode mounts → unmounts → remounts in dev. The abort cleanup between the two mounts cancels the first fetch. This is expected — the second mount starts a fresh request. Ensure your cleanup is correct and the AbortError is handled.


Browser support snapshot

Live support matrix for abortcontroller from Can I Use.

Show static fallback image Data on support for abortcontroller across major browsers from caniuse.com

Source: caniuse.com

Canvas is not supported in your browser