Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cancel async Axios GET request - React hooks

I have an Axios GET request that starts fetching an API when clicking a button. This request takes a long time (around a minute) and I would like to cancel it and start a new one when the button is clicked again.

Also, I would also like to be able to cancel the request if it is pending and I refresh the page.

I tried everything I found here on SO but nothing seems to work. I also read the Axios doc about cancelling : https://github.com/axios/axios#cancellation.

All this lead me there :

Updated code (see edit below)

const CancelToken = axios.CancelToken;
let cancel;

function App() {
    useEffect(() => {
        async function getPairs() {
            try {
                if (cancel) {
                    cancel();
                }

                const res = await axios.get('http://localhost:3000/binance/pairs?timeframe='+timeframe+'&strat='+strat, {
                    cancelToken: new CancelToken(function executor(c) {
                    cancel = c;
                    })
                });

                if (res.status === 200 || res.data.status === 'success') {
                    setPairs(res.data.pairs);
                    setReloadConfig(false);
                }
            }
            catch (err) {
                if (axios.isCancel(err)) {
                    console.log("cancelled");
                } else {
                throw err;
                }
            }
        }
    
        // reloadConfig is a React useState hook that is set to true when clicking a button 
        if (reloadConfig) {
            getPairs();
        }
    }, [reloadConfig]);
}

export default App;

Note that reloadConfig is a React useState hook that is set to true when clicking a button, therefore triggering the useEffect hook and the Axios request.

Problem is, if I click the button multiple times, no request is cancelled and a new one is created every time. I can see all the requests being treated in my terminal.

How can I cancel the last pending request and create a new one when clicking the button triggering the useEffect ?

Also, how can I cancel a pending request if I refresh the page ?

----- Edit -----

After twiking a bit with the code, I found out that placing cancelToken and cancel variables before the component function declaration made me move forward. Now, cancel is not undefined anymore and I get the cancelled message from the catch block :

catch (err) {
    if (axios.isCancel(err)) {
        console.log("cancelled");
    }
}

If I console.log(cancel), it returns this :

ƒ cancel(message) {
    if (token.reason) {
      // Cancellation has already been requested
      return;
    }

    token.reason = new Cancel(message);
    resolvePromise(token.reason);
}

So it tells me cancellation was successful (if I understand correctly ?), but I can still see all the requests being logged in the backend terminal and still being processed. What's wrong with it ?

Thank you very much

----- Edit 2 -----

The solutions showed above all worked. The problem was a misunderstanding on my part : when I do a call to my API to fetch the results, the results are actually fetched by a library that itself fetches one URI per result, meaning that one call to my API makes multiple calls to another API via said library.

I can say the solutions showed above by the community worked because I tried them on a sandbox with a "normal" mock API. I think what happens in my case is that, the library I use with my API calls +200 URLs, so I guess one call was cancelled but not the other 199.

If anyone have a solution to cancel everything that would be great but I think that's more of a backend situation than frontend one at this point.

Thanks to everyone for your help and patience.

like image 502
Tom687 Avatar asked Sep 03 '26 20:09

Tom687


1 Answers

Since you are depending on useEffect to fire up your request, you can use the cleanup function of useEffect to cancel your API request before a new one is executed.

function App() {
    useEffect(() => {
        let CancelToken = axios.CancelToken;
        let source = CancelToken.source();
        async function getPairs() {
            try {

                const res = await axios.get('http://localhost:3000/binance/pairs?timeframe='+timeframe+'&strat='+strat, {
                    cancelToken: source.token
                });

                if (res.status === 200 || res.data.status === 'success') {
                    setPairs(res.data.pairs);
                    setReloadConfig(false);
                }
            }
            catch (err) {
                if (axios.isCancel(err)) {
                    console.log("cancelled");
                } else {
                throw err;
                }
            }
        }
    
        // reloadConfig is a React useState hook that is set to true when clicking a button 
        if (reloadConfig) {
            getPairs();
        }

        return () => {
             source.cancel('Cancelled due to stale request');
        }
    }, [reloadConfig]);
}

export default App;

Note that you should define your cancel variable within useEffect otherwise it will be re-initialzed to undefined on next render if you directly define it within the component.

like image 143
Shubham Khatri Avatar answered Sep 06 '26 11:09

Shubham Khatri