This is more of a general Javascript question,. React itself has little to no opinion on how your fetch requests are made. My experience is that most devs just ignore the response from a request that isn't relevant anymore, which does work most of the time, but if you want to be really clean about it you're looking for the AbortController API: https://developer.mozilla.org/en-US/docs/Web/API/AbortController Answer from AiexReddit on reddit.com
🌐
MDN Web Docs
developer.mozilla.org › en-US › docs › Web › API › AbortController › abort
AbortController: abort() method - Web APIs - MDN Web Docs
The abort() method of the AbortController interface aborts an asynchronous operation before it has completed. This is able to abort fetch requests, the consumption of any response bodies, or streams.
🌐
Medium
medium.com › codex › resilient-fetch-requests-in-javascript-with-abortcontroller-a-guide-with-react-examples-573dba8a3758
Resilient Fetch Requests in JavaScript with AbortController: A Guide with React Examples | by Tawan | CodeX | Medium
April 15, 2023 - In conclusion, the AbortController interface is a powerful tool that can help you write more resilient and efficient code when making fetch requests in JavaScript. It allows you to cancel an ongoing request at any point in time, without the need for workarounds like using a timeout or ignoring the response...
Discussions

Do I need to manage fetch cancellations? Or does React handle this?
This is more of a general Javascript question,. React itself has little to no opinion on how your fetch requests are made. My experience is that most devs just ignore the response from a request that isn't relevant anymore, which does work most of the time, but if you want to be really clean about it you're looking for the AbortController API: https://developer.mozilla.org/en-US/docs/Web/API/AbortController More on reddit.com
🌐 r/reactjs
8
18
September 22, 2021
android - Cancel a fetch request in react-native - Stack Overflow
The support landed in RN 0.60.0 and you can find on my blog an article about this and another one that will give you a simple code to get you started on making abortable requests (and more) in React Native too. More on stackoverflow.com
🌐 stackoverflow.com
reactjs - Aborting OLD API Fetch request REACT - Stack Overflow
In my application, There is a textbox, if the user enters any location then the application searches for that location and creates 5 fetch API request with 5 seconds of interval if requestId :100 t... More on stackoverflow.com
🌐 stackoverflow.com
March 2, 2021
reactjs - How to cancel a fetch on componentWillUnmount - Stack Overflow
I think the title says it all. The yellow warning is displayed every time I unmount a component that is still fetching. Console Warning: Can't call setState (or forceUpdate) on an unmounted comp... More on stackoverflow.com
🌐 stackoverflow.com
🌐
GitHub
github.com › mauricedb › use-abortable-fetch
GitHub - mauricedb/use-abortable-fetch: React hook that does a fetch and aborts when the components is unloaded or a different request is made
npm install use-abortable-fetch or yarn add use-abortable-fetch · import React from 'react'; import useAbortableFetch from 'use-abortable-fetch'; const ChuckNorrisJoke = () => { const { data, loading, error, abort } = useAbortableFetch( '//api.icndb.com/jokes/random/?limitTo=[nerdy]&escape=javascript' ); if (loading) return <div>Loading...</div>; if (error) return <div>Error: {error.message}</div>; if (!data) return null; return <div>Joke: {data.value.joke}</div>; }; export default ChuckNorrisJoke;
Starred by 161 users
Forked by 5 users
Languages   TypeScript 58.3% | JavaScript 37.5% | HTML 4.2% | TypeScript 58.3% | JavaScript 37.5% | HTML 4.2%
🌐
Westbrookdaniel
westbrookdaniel.com › blog › react-abort-controllers
Using AbortControllers to Cancel Fetch in React - Daniel Westbrook
Here we use the web api AbortController as the signal for fetch. By returning a function from useEffect we can trigger the abort controller on dismount (see the React docs).
🌐
Reddit
reddit.com › r/reactjs › do i need to manage fetch cancellations? or does react handle this?
r/reactjs on Reddit: Do I need to manage fetch cancellations? Or does React handle this?
September 22, 2021 -

Let's say I have a request which takes enough time for a user to navigate away before completion (maybe they change there mind, etc), I want this request to be cancelled to prevent unwanted behaviour. Does React automatically handle this? Or do I need to somehow cancel the fetch request?

In my case I have a modal with user authentication form, and am trying to figure out how to cancel the request if the user closes the modal before the request finishes, although this is not an issue with this modal as the response time is <1s I could see this occurring at some point in the future with other request types. I don't want the user to close the modal and the request to still complete as that does not make sense.

Any ideas would be great, I feel I am probably overthinking this.

Top answer
1 of 4
15

You don't need any polyfill anymore for abort a request in React Native 0.60 changelog

Here is a quick example from the doc of react-native:

/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @format
 * @flow
*/

'use strict';

const React = require('react');
const {Alert, Button, View} = require('react-native');

class XHRExampleAbortController extends React.Component<{}, {}> {
  _timeout: any;

  _submit(abortDelay) {
    clearTimeout(this._timeout);
    // eslint-disable-next-line no-undef
    const abortController = new AbortController();
    fetch('https://facebook.github.io/react-native/', {
      signal: abortController.signal,
    })
      .then(res => res.text())
      .then(res => Alert.alert(res))
      .catch(err => Alert.alert(err.message));
    this._timeout = setTimeout(() => {
          abortController.abort();
    }, abortDelay);
  }

  componentWillUnmount() {
    clearTimeout(this._timeout);
  }

  render() {
    return (
      <View>
        <Button
          title="Abort before response"
          onPress={() => {
            this._submit(0);
          }}
        />
        <Button
          title="Abort after response"
          onPress={() => {
            this._submit(5000);
          }}
        />
      </View>
    );
  }
}

module.exports = XHRExampleAbortController;
2 of 4
8

I've written quite a bit actually about this subject. You can also find the first issue about the OLD lack of AbortController in React Native opened by me here

The support landed in RN 0.60.0 and you can find on my blog an article about this and another one that will give you a simple code to get you started on making abortable requests (and more) in React Native too. It also implements a little polyfill for non supporting envs (RN < 0.60 for example).

🌐
Robwise
robwise.github.io › blog › cancel-whatwg-fetch-requests-in-react
Aborting Fetch Requests in React
February 22, 2018 - The way you cancel a fetch request is using a new API called AbortController. You will most likely want to use this polyfill until browser support gets a little better. Note that this doesn’t actually truly implement AbortController but rather throws an error when you try to cancel.
Find elsewhere
🌐
The New Stack
thenewstack.io › home › cancel asynchronous react app requests with abortcontroller
Cancel Asynchronous React App Requests with AbortController - The New Stack
April 24, 2024 - AbortController creates a signal that can be passed to the Fetch API or other APIs that support terminating web requests. You can call the abort method on the AbortController instance when you want to stop the ongoing operation.
🌐
Carl Rippon
carlrippon.com › cancelling-fetch-in-react-and-typescript
Cancelling fetch in React and TypeScript
February 24, 2021 - When AbortController.abort is called, the fetch request is cancelled.
🌐
Plain English
plainenglish.io › blog › how-to-cancel-fetch-and-axios-requests-in-react-useeffect-hook
How to Cancel Fetch and Axios Requests in React’s useEffect Hook
October 20, 2023 - Cleanup Matters: Always consider the cleanup of side effects when making network requests in React components. The useEffect hook provides an excellent place to manage these effects. AbortController (Fetch & Axios) and axios.CancelToken (Axios): These tools are your allies in cancelling requests gracefully when components unmount or when you need to abort ongoing requests.
🌐
Medium
pgarciacamou.medium.com › using-abortcontroller-with-fetch-api-and-reactjs-8d4177e51270
Using AbortController with Fetch API and ReactJS. | by Pablo Garcia | Medium
June 3, 2022 - The trick relies on having an internal Map of all the different AbortControllers so that we can abort them at any time manually using abortRequestSafe by passing the unique id (when a user clicks a button, etc) or automatically every time you call fetchRequest with the same unique ID because the previous AbortController will be aborted and replaced by a new controller using abortAndGetSignalSafe. We can use this useComponentWillUnmount react hook:
🌐
j-labs
j-labs.pl › home › tech blog › how to use the useeffect hook with the abortcontroller
AbortController in React. How to use the useEffect hook with the AbortControl? | j‑labs
December 9, 2025 - It has a single property: – aborted: This property is a Boolean that indicates whether the associated operation has been aborted (true) or not (false). The useEffect hook in React allows us to perform side effects, such as fetching data, when ...
🌐
Giacomocerquone
giacomocerquone.com › blog › aborting-fetch-react-native
Aborting requests in React Native
Well, it turns out that the XHR obscene syntax had an abort method used to terminate connections. While this could be a good approach to support old browsers when you’re doing a web app, this isn’t a viable option in React Native since only fetch is baked inside the core.
Top answer
1 of 16
119

When you fire a Promise it might take a few seconds before it resolves and by that time user might have navigated to another place in your app. So when Promise resolves setState is executed on unmounted component and you get an error - just like in your case. This may also cause memory leaks.

That's why it is best to move some of your asynchronous logic out of components.

Otherwise, you will need to somehow cancel your Promise. Alternatively - as a last resort technique (it's an antipattern) - you can keep a variable to check whether the component is still mounted:

componentDidMount(){
  this.mounted = true;

  this.props.fetchData().then((response) => {
    if(this.mounted) {
      this.setState({ data: response })
    }
  })
}

componentWillUnmount(){
  this.mounted = false;
}

I will stress that again - this is an antipattern but may be sufficient in your case (just like they did with Formik implementation).

A similar discussion on GitHub

EDIT:

This is probably how would I solve the same problem (having nothing but React) with Hooks:

OPTION A:

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

export default function Page() {
  const value = usePromise("https://something.com/api/");
  return (
    <p>{value ? value : "fetching data..."}</p>
  );
}

function usePromise(url) {
  const [value, setState] = useState(null);

  useEffect(() => {
    let isMounted = true; // track whether component is mounted

    request.get(url)
      .then(result => {
        if (isMounted) {
          setState(result);
        }
      });

    return () => {
      // clean up
      isMounted = false;
    };
  }, []); // only on "didMount"

  return value;
}

OPTION B: Alternatively with useRef which behaves like a static property of a class which means it doesn't make component rerender when it's value changes:

function usePromise2(url) {
  const isMounted = React.useRef(true)
  const [value, setState] = useState(null);


  useEffect(() => {
    return () => {
      isMounted.current = false;
    };
  }, []);

  useEffect(() => {
    request.get(url)
      .then(result => {
        if (isMounted.current) {
          setState(result);
        }
      });
  }, []);

  return value;
}

// or extract it to custom hook:
function useIsMounted() {
  const isMounted = React.useRef(true)

  useEffect(() => {
    return () => {
      isMounted.current = false;
    };
  }, []);

  return isMounted; // returning "isMounted.current" wouldn't work because we would return unmutable primitive
}

Example: https://codesandbox.io/s/86n1wq2z8

2 of 16
31

The friendly people at React recommend wrapping your fetch calls/promises in a cancelable promise. While there is no recommendation in that documentation to keep the code separate from the class or function with the fetch, this seems advisable because other classes and functions are likely to need this functionality, code duplication is an anti-pattern, and regardless the lingering code should be disposed of or canceled in componentWillUnmount(). As per React, you can call cancel() on the wrapped promise in componentWillUnmount to avoid setting state on an unmounted component.

The provided code would look something like these code snippets if we use React as a guide:

const makeCancelable = (promise) => {
    let hasCanceled_ = false;

    const wrappedPromise = new Promise((resolve, reject) => {
        promise.then(
            val => hasCanceled_ ? reject({isCanceled: true}) : resolve(val),
            error => hasCanceled_ ? reject({isCanceled: true}) : reject(error)
        );
    });

    return {
        promise: wrappedPromise,
        cancel() {
            hasCanceled_ = true;
        },
    };
};

const cancelablePromise = makeCancelable(fetch('LINK HERE'));

constructor(props){
    super(props);
    this.state = {
        isLoading: true,
        dataSource: [{
            name: 'loading...',
            id: 'loading',
        }]
    }
}

componentDidMount(){
    cancelablePromise.
        .then((response) => response.json())
        .then((responseJson) => {
            this.setState({
                isLoading: false,
                dataSource: responseJson,
            }, () => {

            });
        })
        .catch((error) =>{
            console.error(error);
        });
}

componentWillUnmount() {
    cancelablePromise.cancel();
}

---- EDIT ----

I have found the given answer may not be quite correct by following the issue on GitHub. Here is one version that I use which works for my purposes:

export const makeCancelableFunction = (fn) => {
    let hasCanceled = false;

    return {
        promise: (val) => new Promise((resolve, reject) => {
            if (hasCanceled) {
                fn = null;
            } else {
                fn(val);
                resolve(val);
            }
        }),
        cancel() {
            hasCanceled = true;
        }
    };
};

The idea was to help the garbage collector free up memory by making the function or whatever you use null.

🌐
Medium
medium.com › @devxprite › using-abortcontroller-with-fetch-api-and-reactjs-70c4a75c99e2
Using AbortController with Fetch API and ReactJS | by Prateek Singh | Medium
February 17, 2025 - To abort the fetch request, you call the abort method on the AbortController instance. Let’s create a React component for searching products.
🌐
Medium
medium.com › @rakeshraj2097 › efficient-request-handling-in-react-with-axios-and-abortcontroller-e47bafab87c9
Efficient Request Handling in React with Axios and AbortController | by Rakeshraj | Medium
June 12, 2024 - const useFetchApi = () => { const [fetchData, setFetchData] = useState(null); const [error, setError] = useState(null); const [loading, setLoading] = useState(false); const baseURL ="https://jsonplaceholder.typicode.com/" // Reference for AbortController const abortControllerRef = useRef(null); const handleApi = useCallback(async (url, method = 'GET', data = null) => { setLoading(true); setError(null); // checking the presence of previous request if (abortControllerRef.current) { abortControllerRef.current.abort(); } abortControllerRef.current = new AbortController(); // Getting Signal to Abor
🌐
JavaScript.info
javascript.info › tutorial › network requests
Fetch: Abort
April 13, 2022 - It will listen to abort events on signal. ... We’re done: fetch gets the event from signal and aborts the request.
Top answer
1 of 1
5

The main problem with the cancellation is that you're providing the signal to fetch incorrectly. You're providing the signal as a second argument, but fetch expects an "init" object there with a signal property on it.

As @Keith points out, though, your current code is making one call at a time. That will work, but you probably would benefit from doing the calls in parallel.

Something like this:

const getOneResult = async ({ lat, lng }, signal) => {
    try {
        const response = await fetch(
            "http://localhost:5000/category/" +
                lat +
                "/" +
                lng +
                "/" +
                searchCategory, // *** Where does this come from?
            { signal }
        );
        if (signal && signal.aborted) {
            throw new DOMException("Request cancelled", "AbortError");
        }
        if (!response.ok) {
            throw new Error(`HTTP error ${response.status}`);
        }
        return response.json();
    } catch (error) {
        if (error.name === "AbortError") {
            return null;
        }
    }
};
const getNearbyHikes = async (coordinates, signal) => {
    controller.abort();
    controller = new AbortController();
    const results = await Promise.all(
        coordinates.map((coord) => getOneResult(coord.coordinates, signal))
    );
    if (signal && signal.aborted) {
        return;
    }
    const businesses = [];
    for (const result of results) {
        businesses.push(...result.businesses);
    }
    setHikes((prevState) => [...prevState, ...businesses]);
};

Finally, I would have the caller supply the signal rather than using a global one for all calls to getHikes:

const getOneResult = async ({ lat, lng }, signal) => {
    try {
        const response = await fetch(
            "http://localhost:5000/category/" +
                lat +
                "/" +
                lng +
                "/" +
                searchCategory, // *** Where does this come from?
            { signal }
        );
        if (signal && signal.aborted) {
            throw new DOMException("Request cancelled", "AbortError");
        }
        if (!response.ok) {
            throw new Error(`HTTP error ${response.status}`);
        }
        return response.json();
    } catch (error) {
        if (error.name === "AbortError") {
            return null;
        }
    }
};
let controller = new AbortController();
const getNearbyHikes = async (coordinates) => {
    controller.abort();
    controller = new AbortController();
    const results = await Promise.all(
        coordinates.map((coord) => getOneResult(coord.coordinates, signal))
    );
    if (signal && signal.aborted) {
        return;
    }
    const businesses = [];
    for (const result of results) {
        businesses.push(...result.businesses);
    }
    setHikes((prevState) => [...prevState, ...businesses]);
};

then the caller controls aborting the request. But the former may be fine for your use case.


Side note: You'll see I've added a check for response.ok in there. Your code was assuming that since the fetch promise was fulfilled, the HTTP call worked, but unfortunately that's a footgun in the fetch API: it only rejects its promise on network failure, not HTTP failure. You have to check for the latter explicitly.