Not canceling API requests on unmount leading to memory leaks

Loading

Not canceling API requests when a component unmounts can indeed lead to memory leaks, especially in React applications. This happens because the request might still be active even after the component is no longer in the DOM, and any attempt to update the state of an unmounted component will result in warnings or errors.

Why This Causes Memory Leaks:

  1. Orphaned Network Requests: The request continues to run in the background, consuming resources.
  2. State Updates on Unmounted Components: If the request resolves after unmounting, attempting to update the state triggers React warnings.

How to Prevent Memory Leaks

1. Using AbortController (Recommended for Fetch API)

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

function DataFetcher() {
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);

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

    fetch('https://api.example.com/data', { signal })
      .then((response) => response.json())
      .then((data) => setData(data))
      .catch((err) => {
        if (err.name !== 'AbortError') {
          setError(err);
        }
      });

    return () => {
      controller.abort(); // Cancels the request on unmount
    };
  }, []);

  if (error) return <div>Error: {error.message}</div>;
  if (!data) return <div>Loading...</div>;

  return <div>Data: {JSON.stringify(data)}</div>;
}

2. Cancelling Requests with Axios

If you’re using Axios, you can use CancelToken:

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

function DataFetcher() {
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    const source = axios.CancelToken.source();

    axios.get('https://api.example.com/data', {
      cancelToken: source.token,
    })
    .then((response) => setData(response.data))
    .catch((err) => {
      if (!axios.isCancel(err)) {
        setError(err);
      }
    });

    return () => {
      source.cancel('Request canceled due to component unmount.');
    };
  }, []);

  if (error) return <div>Error: {error.message}</div>;
  if (!data) return <div>Loading...</div>;

  return <div>Data: {JSON.stringify(data)}</div>;
}

3. Using useRef for AbortController (for Multiple Requests)

When you have multiple requests, you might want to track them using useRef:

import React, { useEffect, useRef } from 'react';

function DataFetcher() {
  const controllers = useRef([]);

  useEffect(() => {
    const controller1 = new AbortController();
    const controller2 = new AbortController();
    controllers.current.push(controller1, controller2);

    fetch('https://api.example.com/data1', { signal: controller1.signal });
    fetch('https://api.example.com/data2', { signal: controller2.signal });

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

  return <div>Fetching data...</div>;
}

Key Takeaways:

  • Always cancel pending API requests when the component unmounts.
  • Use AbortController for native fetch or Axios’ CancelToken for Axios requests.
  • Consider using useRef to manage multiple active requests.

Leave a Reply

Your email address will not be published. Required fields are marked *