Canceling an Axios REST call in React Hooks useEffects cleanup failing
The issue in your case is that on a fast network the requests results in a response quickly and it doesn't allow you to click the button. On a throttled network which you can achieve via ChromeDevTools, you can visualise this behaviour correctly
Secondly, when you try to navigate away using window.location.href = 'away link'
react doesn't have a change to trigger/execute the component cleanup and hence the cleanup function of useEffect
won't be triggered.
Making use of Router works
import React from 'react'
import ReactDOM from 'react-dom'
import {BrowserRouter as Router, Switch, Route} from 'react-router-dom'
import useAxiosFetch from './useAxiosFetch'
function App(props) {
const url = 'https://www.siliconvalley-codecamp.com/rest/session/arrayonly'
const {data, loading} = useAxiosFetch(url)
// setTimeout(() => {
// window.location.href = 'https://www.google.com/';
// }, 1000)
if (loading) {
return (
<div>
Loading...
<br />
<button
onClick={() => {
props.history.push('/home')
}}
>
switch away
</button>
</div>
)
} else {
return <div>{JSON.stringify(data)}</div>
}
}
ReactDOM.render(
<Router>
<Switch>
<Route path="/home" render={() => <div>Hello</div>} />
<Route path="/" component={App} />
</Switch>
</Router>,
document.getElementById('root'),
)
You can check the demo
working correctly on a slow network
Based on Axios documentation cancelToken is deprecated and starting from v0.22.0 Axios supports AbortController to cancel requests in fetch API way:
//...
React.useEffect(() => {
const controller = new AbortController();
axios.get('/foo/bar', {
signal: controller.signal
}).then(function(response) {
//...
});
return () => {
controller.abort();
};
}, []);
//...
Here is the final code with everything working in case someone else comes back.
import {useState, useEffect} from "react";
import axios, {AxiosResponse} from "axios";
const useAxiosFetch = (url: string, timeout?: number) => {
const [data, setData] = useState<AxiosResponse | null>(null);
const [error, setError] = useState(false);
const [errorMessage, setErrorMessage] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
let unmounted = false;
let source = axios.CancelToken.source();
axios.get(url, {
cancelToken: source.token,
timeout: timeout
})
.then(a => {
if (!unmounted) {
// @ts-ignore
setData(a.data);
setLoading(false);
}
}).catch(function (e) {
if (!unmounted) {
setError(true);
setErrorMessage(e.message);
setLoading(false);
if (axios.isCancel(e)) {
console.log(`request cancelled:${e.message}`);
} else {
console.log("another error happened:" + e.message);
}
}
});
return function () {
unmounted = true;
source.cancel("Cancelling in cleanup");
};
}, [url, timeout]);
return {data, loading, error, errorMessage};
};
export default useAxiosFetch;