Reputation: 628
I have api file with requests
import * as axios from "axios";
export const productAPI = {
getProducts() {
return axios({
method: 'get',
url: `/api/products`
});
}
};
which reaches to transport.js and sends request(i think that part is not important).
Method above is called from my component like this
useEffect(()=> {
setLoading(true);
productAPI.getProducts()
.then((response) => {
if(response.status === 200) {
history.push(`${pathWithLocation}${PAGES.newLoan}`);
}
})
.catch((error) => {
if (error.response.data.error.message) {
dispatch(addModal({
type: 'basic',
size: 'middle',
title: 'some title',
text: error.response.data.error.message,
buttons: [{ buttonLabel: 'ОК', onClick: ()=> dispatch(removeModal()) }]
}))
}
})
.finally(() => {
setLoading(false);
});
},[])
I want to cancel this specific request when component is unmounted. (switched route for example)
Upvotes: 0
Views: 799
Reputation: 1597
getProducts(cancelToken) {
return axios({
method: 'get',
url: `/api/products`,
cancelToken
});
}
useEffect(()=> {
const source= CancelToken.source();
const isMounted= true;
setLoading(true);
productAPI.getProducts(source.token)
.then((response) => {
if(response.status === 200) {
history.push(`${pathWithLocation}${PAGES.newLoan}`);
}
})
.catch((error) => {
if (error.response.data.error.message) {
dispatch(addModal({
...
}))
}
})
.finally(() => {
isMounted && setLoading(false);
});
return ()=>{
isMounted= false;
source.cancel();
}
},[])
Or a bit magic way (Codesandbox demo):
import React, { useState } from "react";
import {
useAsyncEffect,
CanceledError,
E_REASON_UNMOUNTED
} from "use-async-effect2";
import cpAxios from "cp-axios";
export default function TestComponent(props) {
const [text, setText] = useState("");
const [loading, setLoading] = useState(true);
const cancel = useAsyncEffect(
function* () {
try {
const response = yield cpAxios(props.url);
setText(JSON.stringify(response.data));
setLoading(false);
if (response.status === 200) {
//history.push(`${pathWithLocation}${PAGES.newLoan}`);
}
} catch (err) {
CanceledError.rethrow(err, E_REASON_UNMOUNTED);
setLoading(false);
setText(err.toString());
//dispatch(addModal({})
}
},
[props.url]
);
return (
<div className="component">
<div className="caption">useAsyncEffect demo:</div>
<div>{text}</div>
<button onClick={cancel} disabled={!loading}>
Cancel request
</button>
</div>
);
}
Upvotes: 0
Reputation: 6146
You can just use a isCurrent
flag. (I have to admit that I have not considered what the benefit of using the axios.cancelToken mechanism would be here. Maybe it would make it cleaner, maybe it would just make it more convoluted.)
useEffect(() => {
const isCurrent = true;
setLoading(true);
productAPI.getProducts()
.then((response) => {
if(isCurrent && response.status === 200) {
history.push(`${pathWithLocation}${PAGES.newLoan}`);
}
})
.catch((error) => {
if (isCurrent && error.response.data.error.message) {
dispatch(addModal({/*...*/}))
}
})
.finally(() => {
if (isCurrent) setLoading(false);
});
return () => { isCurrent = false };
}, [])
Upvotes: 1