How to Abort Axios Requests on Component Unmount

When developing modern frontend applications, navigating away from a view while asynchronous API requests are still in flight can cause memory leaks, unnecessary resource consumption, and unwanted state updates on unmounted components. This guide demonstrates how to use the standard AbortController interface with the Axios HTTP client to cleanly and automatically cancel pending requests during a component's unmount lifecycle.

Understanding AbortController in Axios

Starting with Axios version 0.22.0, Axios natively supports the web-standard AbortController API, replacing the older and deprecated CancelToken mechanism. AbortController allows you to create a signal that can be passed to one or more HTTP requests, giving you a centralized way to terminate pending network calls on demand.

Basic Implementation in React

In React, the cleanup function of the useEffect hook runs when the component unmounts. This makes it the ideal location to call .abort() on your controller instance.

import React, { useEffect, useState } from 'react';
import axios from 'axios';

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

  useEffect(() => {
    // 1. Instantiate the AbortController
    const controller = new AbortController();

    const fetchUserData = async () => {
      try {
        setLoading(true);
        // 2. Pass the controller's signal to the request configuration
        const response = await axios.get(`/api/users/${userId}`, {
          signal: controller.signal
        });
        setUser(response.data);
      } catch (error) {
        // 3. Check if the error is due to cancellation
        if (axios.isCancel(error)) {
          console.log('Request canceled:', error.message);
        } else {
          console.error('An unexpected error occurred:', error);
        }
      } finally {
        setLoading(false);
      }
    };

    fetchUserData();

    // 4. Clean up and abort pending requests when the component unmounts
    return () => {
      controller.abort();
    };
  }, [userId]);

  if (loading) return <div>Loading...</div>;
  return <div>{user ? user.name : 'No user data'}</div>;
}

export default UserProfile;

Aborting Multiple Requests Simultaneously

A single AbortController instance can control multiple concurrent requests. When you pass the same signal to multiple Axios calls, executing controller.abort() terminates every pending request associated with that signal at once.

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

  const loadDashboardData = async () => {
    try {
      const [stats, activities] = await Promise.all([
        axios.get('/api/stats', { signal: controller.signal }),
        axios.get('/api/activities', { signal: controller.signal })
      ]);

      setDashboardData({ stats: stats.data, activities: activities.data });
    } catch (error) {
      if (!axios.isCancel(error)) {
        handleError(error);
      }
    }
  };

  loadDashboardData();

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

Handling Cancellation Errors

When a request is aborted, Axios rejects the promise with a CanceledError. It is essential to differentiate between standard network or server errors and deliberate cancellations to avoid displaying false error messages to the user.