Query Cancellation alone doesn't do much per default: It cancels the Query itself (so data is not stored), but it doesn't abort the request.

To achieve that, you need to forward the AbortSignal provided by react-query to your fetch function. How you do this depends on what you use for data fetching - the docs have multiple examples, but here is the one for fetch:

const query = useQuery({
  queryKey: ['todos'],
  queryFn: async ({ signal }) => {
    const todosResponse = await fetch('/todos', { signal })
    return todoResponse.json()
  }
})

If the signal is passed to fetch, the Query will be cancelled automatically when the fetch is aborted. This will happen as soon as no component is mounted that is actively using the Query.

Answer from TkDodo on Stack Overflow
🌐
TanStack
tanstack.com › query › v4 › docs › react › guides › query-cancellation
Query Cancellation | TanStack Query Docs
However, if you consume the AbortSignal, the Promise will be cancelled (e.g. aborting the fetch) and therefore, also the Query must be cancelled. Cancelling the query will result in its state being reverted to its previous state. ... An AbortSignal can be set in the client request method.
Discussions

A call to fetchQuery is cancelled when React useQuery is unmounted
The promise was cancelled when some component was using the same query with useQuery - only during development, when using StrictMode. But it can also happen in production. If an useQuery is unmounted, it cancels the query if it is the last one observing the query. More on github.com
🌐 github.com
6
October 22, 2025
How important is it to cancel network requests when a component unmounts?
i personally say it is required. usually when you make a network call you are also dispatching it to some state management. if that is a useState and you have unmounted, you will throw an exception when it attempts to set that state. redux would be fine, recoil would be as well unless the recoil root was also unmounted, same for context. i feel it is just easier to always handle this at the start and not worry about if it was handled correctly later. just my opinion though More on reddit.com
🌐 r/reactjs
11
2
February 19, 2023
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
docs: useQuery abortOnUnmount has more effects than the name and documentation suggest
From reading the React Query docs ... cause react-query to cancel and throw away any in-flight requests for unused queries immediately. It took me a while, but I eventually found out about tRPC's abortOnUnmount, and correctly suspected that setting that option would cause tRPC to consume the signal as I wanted. So the issue is that abortOnUnmount doesn't just cause the query to abort on unmount... More on github.com
🌐 github.com
2
May 29, 2023
🌐
TanStack
tanstack.com › query › v3 › docs › framework › react › guides › query-cancellation
Query Cancellation | TanStack Query React Docs
This is helpful if you've started receiving a query, but then unmount the component before it finishes. If you mount the component again and the query has not been garbage collected yet, data will be available. However, if you consume the AbortSignal or attach a cancel function to your Promise, the Promise will be cancelled (e.g.
🌐
Carl Rippon
carlrippon.com › cancelling-requests-with-react-query
Cancelling Requests with React Query
If we give this a try on a slow connection, we will see the query is cancelled when the cancel button is clicked: ... By default, React Query doesn’t cancel in-flight requests when its component is umounted. However, if the fetching function’s returned promise contains a cancel method, it will invoke this to cancel an in-flight query when its component is unmounted...
🌐
GitHub
github.com › TanStack › query › discussions › 1283
Query Cancellation · TanStack/query · Discussion #1283
Quote reply · - How to cancel the request when the component is unmounted? As far as I'm aware, the cancel method will only be called if your query becomes inactive while the request is still in-flight.
Author   TanStack
🌐
GitHub
github.com › TanStack › query › issues › 9798
A call to fetchQuery is cancelled when React useQuery is unmounted · Issue #9798 · TanStack/query
October 22, 2025 - The promise was cancelled when ... it can also happen in production. If an useQuery is unmounted, it cancels the query if it is the last one observing the query....
Author   bartlangelaan
🌐
Reddit
reddit.com › r/reactjs › how important is it to cancel network requests when a component unmounts?
r/reactjs on Reddit: How important is it to cancel network requests when a component unmounts?
February 19, 2023 -

If a fetch request keeps running after a component has unmounted, we are wasting resources because we don't have a use for the return value of that request anymore (since the component showing the data was removed).

I'm trying to understand if request cancellation is considered merely an "optimization" or absolutely important. Assuming that the request doesn't do anything particularly expensive, just a database request on the backend.

Also, what about POST and PATCH requests? Don't we wanna finish these even if we navigated away?

And what about libraries like React-query and SWR that cache the response? Does the caching make it okay to load an unused response, considering that we can reuse it later to show cached data?

Find elsewhere
🌐
DEV Community
dev.to › serifcolakel › canceling-requests-in-reactreact-native-a-comprehensive-guide-2ami
Canceling Requests in React/React Native: A Comprehensive Guide - DEV Community
April 6, 2025 - Cleanup: In the useEffect cleanup function, we abort any ongoing requests when the component unmounts. In this article, we explored how to cancel requests in React using different libraries and techniques. We covered: Axios: Using Cancel Tokens to cancel requests. Fetch API: Using AbortController to cancel requests. React Query: Built-in support for canceling requests with AbortController.
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.

🌐
TanStack
tanstack.com › query › v5 › docs › framework › angular › guides › query-cancellation
Query Cancellation | TanStack Query Angular Docs
This is helpful if you've started receiving a query, but then unmount the component before it finishes. If you mount the component again and the query has not been garbage collected yet, data will be available. However, if you consume the AbortSignal, the Promise will be cancelled (e.g.
🌐
GitHub
github.com › trpc › trpc › issues › 4448
docs: useQuery abortOnUnmount has more effects than the name and documentation suggest · Issue #4448 · trpc/trpc
May 29, 2023 - The tRPC documentation on the website ... AbortSignal provided by react-query. ... By default, queries that unmount or become unused before their promises are resolved are not cancelled....
Published   May 29, 2023
Author   keanemind
Top answer
1 of 1
2

[...] and navigating back it does not make a new request.

First of all, according to your code, as per the staleTime option that is set as an option on useQuery itself, the cache should invalidate every five seconds. So each time the useQuery hook is mounted (such as on route change), if five seconds have passed, a new request should be made. Your code does appear to be incomplete though as you're referencing id which appears to be undefined.

In any case, since you are requesting details of a resource with an ID, you should consider using a query key like: [planeInfo, id] instead of planeInfo alone. From the documentation:

Since query keys uniquely describe the data they are fetching, they should include any variables you use in your query function that change. For example:

function Todos({ todoId }) {
    const result = useQuery(['todos', todoId], () => 
    fetchTodoById(todoId))
}

To handle canceling the request on navigation:

You can't wrap the useQuery hook from React Query in a useEffect hook, but rather you can use use the return function of useEffect to clean up your useQuery request, effectively canceling the request when the component unmounts. With useQuery there are two ways (possibly more) to cancel a request:

  • use the remove method exposed on the returned object of useQuery
  • use the QueryClient method: cancelQueries

(see: useQuery reference here)

see: QueryClient reference here and specifically cancelQueries

Using remove with useEffect

(I've only kept the relevant bits of your code)

const Component2: React.FunctionComponent <RouteComponentProps<any>> = (props) => {
        const fetchChart = async() => {
          const res = await fetch(`/detail/${id}`);
          return res.json();
        };

        const {
          data,
          status,
          /** access the remove method **/
          remove
        } = useQuery('planeInfo', fetchPlane, {
          staleTime: 5000,
        });

        useEffect(() => {
          /** when this component unmounts, call it **/
          return () => remove()

          /** make sure to set an empty deps array **/
        }, [])

        /** the rest of your component **/

}

Calling remove like this will cancel any ongoing request, but as its name suggests, it also removes the query from the cache. Depending on whether you need to keep the data in cache or not, this may or may not be a viable strategy. If you need to keep the data, you can instead use the canceQueries method.

Using cancelQueries with useEffect

Much like before except here you need to export your queryClient instance from the routing component file (as you have it defined there) and then you're importing that instance of QueryClient into Component2 and calling cancelQueries on the cache key from useEffect:

import { queryClient } from "./routing-component"

const Component2: React.FunctionComponent <RouteComponentProps<any>> = (props) => {
        const fetchChart = async() => {
          const res = await fetch(`/detail/${id}`);
          return res.json();
        };

        const {
          data,
          status,
        } = useQuery(['planeInfo', id], fetchPlane, {
          staleTime: 5000,
        });

        useEffect(() => {
          /** when this component unmounts, call it **/
          return () => queryClient.cancelQueries(['planeInfo', id], {exact: true, fetching: true})
        }, [])

        /** the rest of your component **/
}

Here you see that I've implemented the query key as I suggested before, with the id as well. You can see why having a more precise reference to the cached object can be beneficial. I'm also using two query filters: exact and fetching. Setting exact to true will make sure React Query doesn't use pattern matching and cancel a broader set of queries. You can decide whether or not this is necessary for your implementation needs. Setting fetching to true will make sure React Query includes and cancels and queries that are currently fetching data.

Just note that by depending on useEffect, it is in some cases possible for it's parent component to unmount due to factors other than the user navigating away from the page (such as a modal). In such cases, you should move your useQuery up in the component tree into a component that will only unmount when a user navigates, and then pass the result of useQuery into the child component as props, to avoid premature cancellations.

Alternatively you could use Axios instead of fetch. With Axios you can cancel a request using a global cancel token, and combine executing that cancellation with React Router's useLocation (example here). You could of course also combine useLocation listening to route changes with QueryClient.cancelQueries. There are in fact, many possible approaches to your question.

🌐
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.

🌐
Medium
medium.com › @secret-dev › how-to-auto-cancel-api-requests-when-calls-duplicate-or-route-component-changes-react-18-asp-net-a9ef5ae53623
How to Auto‑Cancel API Requests When Calls Duplicate or Route/Component Changes (React 18 + ASP.NET Core 7) | by Secret Dev | Medium
August 28, 2025 - Use the browser’s AbortController (or libraries that expose it), build a tiny in‑flight request registry to dedupe and “takeLatest,” hook cancellation into React Router unmounts and effect cleanups, and make your ASP.NET Core 7 endpoints observe CancellationToken so the server actually stops work when the client goes away.
🌐
Hamon
hamon.in › blog › mastering-abortcontroller-in-react-canceling-requests-made-simple
Mastering AbortController in React: Canceling Requests ...
Let’s start with a simple fetch request in a React component. We’ll use AbortController to cancel it when the component unmounts.
🌐
Medium
santoshsubedi.medium.com › cancel-axios-request-on-component-unmount-react-js-hooks-981beb008c4c
Cancel Axios request on component unmount | React.js (Hooks) | by Santosh Subedi | Medium
April 24, 2021 - Here I wanna make the explanation simple. So what mount and unmount means? Mount: Show Unmount: Hide ... So now in order to stop products fetch, we are going to cancel the axios request by using axios cancel token as below.
🌐
GitHub
github.com › TanStack › query › discussions › 2422
The easy way to remove a query cache after unmounting · TanStack/query · Discussion #2422
In dev tools the network tab shows an unending stream of requests for http://localhost:8080/endpoint?param=null. const MyComponent= (): ReactElement => { const { data, isLoading } = useQuery( 'someKey', async () => { const options = { url: 'http://localhost:8080/endpoint?param=null', method: 'GET', headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, }; const response = await fetch(options.url, options); if (response.ok) { return response.json(); } return null; }, { cacheTime: 0, retry: false, refetchOnMount: false, retryOnMount: false } ); return ( <> { !isLoading && <PageContainer> { data?
Author   TanStack
🌐
Reddit
reddit.com › r/reactjs › in a filter search ui how important is it cancel requests when user makes quick changes?
In a filter search UI how important is it cancel requests when user makes quick changes? : r/reactjs
September 19, 2024 - Join the Reactiflux Discord (reactiflux.com) for additional React discussion and help. ... Sorry, this post was deleted by the person who originally posted it. Share ... Unless you actually get the server to cancel processing, it doesn't matter if you just forget you made the request and use the new one, or cancelling an inflight request.