how to cancel/abort ajax request in axios - reactjs

I use axios for ajax requests and reactJS + flux for render UI. In my app there is third side timeline (reactJS component). Timeline can be managed by mouse's scroll. App sends ajax request for the actual data after any scroll event. Problem that processing of request at server can be more slow than next scroll event. In this case app can have several (2-3 usually) requests that already is deprecated because user scrolls further. it is a problem because every time at receiving of new data timeline begins redraw. (Because it's reactJS + flux) Because of this, the user sees the movement of the timeline back and forth several times. The easiest way to solve this problem, it just abort previous ajax request as in jQuery. For example:
$(document).ready(
var xhr;
var fn = function(){
if(xhr && xhr.readyState != 4){
xhr.abort();
}
xhr = $.ajax({
url: 'ajax/progress.ftl',
success: function(data) {
//do something
}
});
};
var interval = setInterval(fn, 500);
);
How to cancel/abort requests in axios?

Axios does not support canceling requests at the moment. Please see this issue for details.
UPDATE: Cancellation support was added in axios v0.15.
EDIT: The axios cancel token API is based on the withdrawn cancelable promises proposal.
UPDATE 2022: Starting from v0.22.0 Axios supports AbortController to cancel requests in fetch API way:
Example:
const controller = new AbortController();
axios.get('/foo/bar', {
signal: controller.signal
}).then(function(response) {
//...
});
// cancel the request
controller.abort()

Using useEffect hook:
useEffect(() => {
const ourRequest = Axios.CancelToken.source() // <-- 1st step
const fetchPost = async () => {
try {
const response = await Axios.get(`endpointURL`, {
cancelToken: ourRequest.token, // <-- 2nd step
})
console.log(response.data)
setPost(response.data)
setIsLoading(false)
} catch (err) {
console.log('There was a problem or request was cancelled.')
}
}
fetchPost()
return () => {
ourRequest.cancel() // <-- 3rd step
}
}, [])
Note: For POST request, pass cancelToken as 3rd argument
Axios.post(`endpointURL`, {data}, {
cancelToken: ourRequest.token, // 2nd step
})

Typically you want to cancel the previous ajax request and ignore it's coming response, only when a new ajax request of that instance is started, for this purpose, do the following:
Example: getting some comments from API:
// declare an ajax request's cancelToken (globally)
let ajaxRequest = null;
function getComments() {
// cancel previous ajax if exists
if (ajaxRequest ) {
ajaxRequest.cancel();
}
// creates a new token for upcomming ajax (overwrite the previous one)
ajaxRequest = axios.CancelToken.source();
return axios.get('/api/get-comments', { cancelToken: ajaxRequest.token }).then((response) => {
console.log(response.data)
}).catch(function(err) {
if (axios.isCancel(err)) {
console.log('Previous request canceled, new request is send', err.message);
} else {
// handle error
}
});
}

import React, { Component } from "react";
import axios from "axios";
const CancelToken = axios.CancelToken;
let cancel;
class Abc extends Component {
componentDidMount() {
this.Api();
}
Api() {
// Cancel previous request
if (cancel !== undefined) {
cancel();
}
axios.post(URL, reqBody, {
cancelToken: new CancelToken(function executor(c) {
cancel = c;
}),
})
.then((response) => {
//responce Body
})
.catch((error) => {
if (axios.isCancel(error)) {
console.log("post Request canceled");
}
});
}
render() {
return <h2>cancel Axios Request</h2>;
}
}
export default Abc;

There is really nice package with few examples of usage called axios-cancel.
I've found it very helpful.
Here is the link: https://www.npmjs.com/package/axios-cancel

https://github.com/axios/axios#cancellation
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
let url = 'www.url.com'
axios.get(url, {
progress: false,
cancelToken: source.token
})
.then(resp => {
alert('done')
})
setTimeout(() => {
source.cancel('Operation canceled by the user.');
},'1000')

This is how I did it using promises in node. Pollings stop after making the first request.
var axios = require('axios');
var CancelToken = axios.CancelToken;
var cancel;
axios.get('www.url.com',
{
cancelToken: new CancelToken(
function executor(c) {
cancel = c;
})
}
).then((response) =>{
cancel();
})

Using cp-axios wrapper you able to abort your requests with three diffent types of the cancellation API:
1. Promise cancallation API (CPromise):
Live browser example
const cpAxios= require('cp-axios');
const url= 'https://run.mocky.io/v3/753aa609-65ae-4109-8f83-9cfe365290f0?mocky-delay=5s';
const chain = cpAxios(url)
.timeout(5000)
.then(response=> {
console.log(`Done: ${JSON.stringify(response.data)}`)
}, err => {
console.warn(`Request failed: ${err}`)
});
setTimeout(() => {
chain.cancel();
}, 500);
2. Using AbortController signal API:
const cpAxios= require('cp-axios');
const CPromise= require('c-promise2');
const url= 'https://run.mocky.io/v3/753aa609-65ae-4109-8f83-9cfe365290f0?mocky-delay=5s';
const abortController = new CPromise.AbortController();
const {signal} = abortController;
const chain = cpAxios(url, {signal})
.timeout(5000)
.then(response=> {
console.log(`Done: ${JSON.stringify(response.data)}`)
}, err => {
console.warn(`Request failed: ${err}`)
});
setTimeout(() => {
abortController.abort();
}, 500);
3. Using a plain axios cancelToken:
const cpAxios= require('cp-axios');
const url= 'https://run.mocky.io/v3/753aa609-65ae-4109-8f83-9cfe365290f0?mocky-delay=5s';
const source = cpAxios.CancelToken.source();
cpAxios(url, {cancelToken: source.token})
.timeout(5000)
.then(response=> {
console.log(`Done: ${JSON.stringify(response.data)}`)
}, err => {
console.warn(`Request failed: ${err}`)
});
setTimeout(() => {
source.cancel();
}, 500);
4. Usage in a custom React hook (Live Demo):
import React from "react";
import { useAsyncEffect } from "use-async-effect2";
import cpAxios from "cp-axios";
/*
Note: the related network request will be aborted as well
Check out your network console
*/
function TestComponent({ url, timeout }) {
const [cancel, done, result, err] = useAsyncEffect(
function* () {
return (yield cpAxios(url).timeout(timeout)).data;
},
{ states: true, deps: [url] }
);
return (
<div>
{done ? (err ? err.toString() : JSON.stringify(result)) : "loading..."}
<button onClick={cancel} disabled={done}>
Cancel async effect (abort request)
</button>
</div>
);
}
Update
Axios v0.22.0+ supports AbortController natively:
const controller = new AbortController();
axios.get('/foo/bar', {
signal: controller.signal
}).then(function(response) {
//...
});
// cancel the request
controller.abort()

Starting from v0.22.0 Axios supports AbortController to cancel requests in fetch API way:
const controller = new AbortController();
axios.get('/foo/bar', {
signal: controller.signal
}).then(function(response) {
//...
});
// cancel the request
controller.abort()
CancelToken deprecated
You can also cancel a request using a CancelToken.
The axios cancel token API is based on the withdrawn cancelable promises proposal.
This API is deprecated since v0.22.0 and shouldn't be used in new projects
You can create a cancel token using the CancelToken.source factory as shown below:

import {useState, useEffect} from 'react'
export function useProfileInformation({accessToken}) {
const [profileInfo, setProfileInfo] = useState(null)
useEffect(() => {
const abortController = new AbortController()
window
.fetch('https://api.example.com/v1/me', {
headers: {Authorization: `Bearer ${accessToken}`},
method: 'GET',
mode: 'cors',
signal: abortController.signal,
})
.then(res => res.json())
.then(res => setProfileInfo(res.profileInfo))
return function cancel() {
abortController.abort()
}
}, [accessToken])
return profileInfo
}
// src/app.jsx
import React from 'react'
import {useProfileInformation} from './hooks/useProfileInformation'
export function App({accessToken}) {
try {
const profileInfo = useProfileInformation({accessToken})
if (profileInfo) {
return <h1>Hey, ${profileInfo.name}!</h1>
} else {
return <h1>Loading Profile Information</h1>
}
} catch (err) {
return <h1>Failed to load profile. Error: {err.message}</h1>
}
}

Related

Remix.run, Remix-Auth destroy user session if token validation fails

I am using Remix, along with Remix-Auth and using the Twitch API/OAuth, which requires that I check in with their /validate endpoint every hour docs. I had someone recommend that I use a resource route and POST to that if the validation endpoint returned a status of 401, however, I need as I stated before the request needs to be sent every hour I figured maybe I could use something like React-Query to POST to the resource route every hour.
Just pointing out that I use createCookieSessionStorage with Remix Auth to create the session
Problem
I haven't been able to achieve the actual session being destroyed and a user being re-routed to the login page, I have left what actual code I have currently any help or suggestions to actually achieve the session being destroyed and be re-routed to the login page if the validation fails would be greatly appreciated.
// React Query client side, checks if the users token is still valid
const { error, data } = useQuery("TV-Revalidate", () =>
fetch("https://id.twitch.tv/oauth2/validate", {
headers: {
Authorization: `Bearer ${user?.token}`,
},
}).then((res) => res.json())
);
The above React Query returns this
// My attempt at the resource route
// ~routes/auth/destroy.server.ts
import { ActionFunction, redirect } from "#remix-run/node";
import { destroySession, getSession } from "~/services/session.server";
export const action: ActionFunction = async ({request}) => {
const session = await getSession(request.headers.get("cookie"))
return redirect("/login", {
headers: {
"Set-Cookie": await destroySession(session)
}
})
}
// Second attempt at resource route
// ~routes/auth/destroy.server.ts
import { ActionFunction, redirect } from "#remix-run/node";
import { destroySession, getSession } from "~/services/session.server";
export const action: ActionFunction = async ({request}) => {
const session = await getSession(request.headers.get("cookie"))
return destroySession(session)
}
I attempted using an if statement to POST to the resource route or else render the page, however, this definitely won't work as React errors out because functions aren't valid as a child and page is blank.
//index.tsx
export default function Index() {
const { user, bits, vali } = useLoaderData();
console.log("loader", vali);
const { error, data } = useQuery("TV-Revalidate", () =>
fetch("https://id.twitch.tv/oauth2/validate", {
headers: {
Authorization: `Bearer ${user?.token}`,
},
}).then((res) => res.json())
);
if (data?.status === 401)
return async () => {
await fetch("~/services/destroy.server", { method: "POST" });
};
else
return ( ... );}
You could use Remix' useFetcher hook.
https://remix.run/docs/en/v1/api/remix#usefetcher
// Resource route
// routes/api/validate
export const loader: LoaderFunction = async ({ request }) => {
const session = await getSession(request);
try {
const { data } = await fetch("https://id.twitch.tv/oauth2/validate", {
headers: {
Authorization: `Bearer ${session.get("token")}`
}
});
return json({
data
}, {
headers: {
"Set-Cookie": await commitSession(session),
}
});
} catch(error) {
return redirect("/login", {
headers: {
"Set-Cookie": await destroySession(session)
}
});
}
}
And then in your route component something like this:
const fetcher = useFetcher();
useEffect(() => {
if (fetcher.type === 'init') {
fetcher.load('/api/validate');
}
}, [fetcher]);
useEffect(() => {
if(fetcher.data?.someValue {
const timeout = setTimeout(() => fetcher.load('/api/validate'), 1 * 60 * 60 * 1000);
return () => clearTimeout(timeout);
}
},[fetcher.data]);

How to logout automatically when session expires while using createAsyncThunk and axios (withcredential) option using react and redux toolkit?

I am trying to logout the user when the session expires after a certain period of time. I am using redux-toolkit with react for my API calls and, hence, using the createAsyncThunk middleware for doing so.
I have around 60 API calls made in maybe 20 slices throughout my application. Also, there is a async function for logout too that is fired up on the button click. Now the problem that I am facing is that if the session expires, I am not able to logout the user automatically. If I had to give him the message, then I had to take up that message from every api call and make sure that every screen of mine has a logic to notify the Unautherised message.
I did check a method called Polling that calls an API after a certain given time. And I believe that this is not a very efficient way to handle this problem.
**Here is a little code that will help you understand how my API calls are being made in the slices of my application. **
// Here is the custom created api that has axios and withcredentials value
import axios from "axios";
const api = axios.create({
baseURL:
process.env.NODE_ENV === "development" ? process.env.REACT_APP_BASEURL : "",
headers: {
"Content-Type": "application/json",
},
withCredentials: true,
});
export default api;
// My Logout Function!!
export const logoutUser = createAsyncThunk(
"userSlice/logoutUser",
async (thunkAPI) => {
try {
const response = await api.get("/api/admin/logout");
if (response.status === 200) {
return response.data;
} else {
return thunkAPI.rejectWithValue(response.data);
}
} catch (e) {
return thunkAPI.rejectWithValue(e.response.data);
}
}
);
I want to dispatch this function whenever there is a response status-code is 401 - Unauthorised. But I don't want to keep redundant code for all my other API calls calling this function. If there is a middleware that might help handle this, that would be great, or any solution will be fine.
// Rest of the APIs are called in this way.
..........
export const getStatus = createAsyncThunk(
"orgStat/getStatus",
async (thunkAPI) => {
try {
const response = await api.get("/api/admin/orgstat");
if (response.status === 200) {
return response.data;
} else {
return thunkAPI.rejectWithValue(response.data);
}
} catch (e) {
return thunkAPI.rejectWithValue(e.response.data);
}
}
);
const OrgStatusSlice = createSlice({
name: "orgStat",
initialState,
reducers: {
.......
},
extraReducers: {
[getStatus.pending]: (state) => {
state.isFetching = true;
},
[getStatus.rejected]: (state, { payload }) => {
state.isFetching = false;
state.isError = true;
state.isMessage = payload.message;
},
[getStatus.fulfilled]: (state, { payload }) => {
state.isFetching = false;
state.data = payload.data;
},
},
});
.......
If needed any more clearence please comment I will edit the post with the same.
Thank You!!
import axios from 'axios'
import errorParser from '../services/errorParser'
import toast from 'react-hot-toast'
import {BaseQueryFn} from '#reduxjs/toolkit/query'
import {baseQueryType} from './apiService/types/types'
import store from './store'
import {handleAuth} from './common/commonSlice'
import storageService from '#services/storageService'
// let controller = new AbortController()
export const axiosBaseQuery =
(
{baseUrl}: {baseUrl: string} = {baseUrl: ''}
): BaseQueryFn<baseQueryType, unknown, unknown> =>
async ({url, method, data, csrf, params}) => {
const API = axios.create({
baseURL: baseUrl,
})
API.interceptors.response.use(
(res) => {
if (
res.data?.responseCode === 1023 ||
res.data?.responseCode === 6023
) {
if(res.data?.responseCode === 1023){
console.log('session expired')
store.dispatch(handleSession(false))
return
}
console.log('Lopgged in somewhere else')
store.dispatch(handleSession(false))
storageService.clearStorage()
// store.dispatch(baseSliceWithTags.util.resetApiState())
return
// }, 1000)
}
return res
},
(error) => {
const expectedError =
error.response?.status >= 400 &&
error.response?.status < 500
if (!expectedError) {
if (error?.message !== 'canceled') {
toast.error('An unexpected error occurrred.')
}
}
if (error.response?.status === 401) {
// Storage.clearJWTToken();
// window.location.assign('/')
}
return Promise.reject(error)
}
)
try {
let headers = {}
if (csrf) headers = {...csrf}
const result = await API({
url: url,
method,
data,
headers,
params: params ? params : '',
baseURL: baseUrl,
// signal: controller.signal,
})
return {data: result.data}
} catch (axiosError) {
const err: any = axiosError
return {
error: {
status: errorParser.parseError(err.response?.status),
data: err.response?.data,
},
}
}
}
I am also using RTK with Axios. You can refer to the attached image.

Axios throwing CanceledError with Abort controller in react

I have built an axios private instance with interceptors to manage auth request.
The system has a custom axios instance:
const BASE_URL = 'http://localhost:8000';
export const axiosPrivate = axios.create({
baseURL: BASE_URL,
headers: {
'Content-Type': 'application/json',
},
withCredentials: true,
});
A custom useRefreshToken hook returns accessToken using the refresh token:
const useRefreshToken = () => {
const { setAuth } = useAuth();
const refresh = async () => {
const response = await refreshTokens();
// console.log('response', response);
const { user, roles, accessToken } = response.data;
setAuth({ user, roles, accessToken });
// return accessToken for use in axiosClient
return accessToken;
};
return refresh;
};
export default useRefreshToken;
Axios interceptors are attached to this axios instance in useAxiosPrivate.js file to attached accessToken to request and refresh the accessToken using a refresh token if expired.
const useAxiosPrivate = () => {
const { auth } = useAuth();
const refresh = useRefreshToken();
useEffect(() => {
const requestIntercept = axiosPrivate.interceptors.request.use(
(config) => {
// attach the access token to the request if missing
if (!config.headers['Authorization']) {
config.headers['Authorization'] = `Bearer ${auth?.accessToken}`;
}
return config;
},
(error) => Promise.reject(error)
);
const responseIntercept = axiosPrivate.interceptors.response.use(
(response) => response,
async (error) => {
const prevRequest = error?.config;
// sent = custom property, after 1st request - sent = true, so no looping requests
if (error?.response?.status === 403 && !prevRequest?.sent) {
prevRequest.sent = true;
const newAccessToken = await refresh();
prevRequest.headers['Authorization'] = `Bearer ${newAccessToken}`;
return axiosPrivate(prevRequest);
}
return Promise.reject(error);
}
);
// remove the interceptor when the component unmounts
return () => {
axiosPrivate.interceptors.response.eject(responseIntercept);
axiosPrivate.interceptors.request.eject(requestIntercept);
};
}, [auth, refresh]);
return axiosPrivate;
};
export default useAxiosPrivate;
Now, this private axios instance is called in functional component - PanelLayout which is used to wrap around the pages and provide layout.
Here, I've tried to use AbortControllers in axios to terminate the request after the component is mounted.
function PanelLayout({ children, title }) {
const [user, setUser] = useState(null);
const axiosPrivate = useAxiosPrivate();
const router = useRouter();
useEffect(() => {
let isMounted = true;
const controller = new AbortController();
const signal = controller.signal;
const getUserProfile = async () => {
try {
const response = await axiosPrivate.get('/api/identity/profile', {
signal,
});
console.log(response.data);
isMounted && setUser(response.data.user);
} catch (error) {
console.log(error);
router.push({
pathname: '/seller/auth/login',
query: { from: router.pathname },
});
}
};
getUserProfile();
return () => {
isMounted = false;
controller.abort();
};
}, []);
console.log('page rendered');
return (
<div className='flex items-start'>
<Sidebar className='h-screen w-[10rem]' />
<section className='min-h-screen flex flex-col'>
<PanelHeader title={title} classname='left-[10rem] h-[3.5rem]' />
<main className='mt-[3.5rem] flex-1'>{children}</main>
</section>
</div>
);
}
export default PanelLayout;
However, the above code is throwing the following error:
CanceledError {message: 'canceled', name: 'CanceledError', code: 'ERR_CANCELED'}
code: "ERR_CANCELED"
message: "canceled"
name: "CanceledError"
[[Prototype]]: AxiosError
constructor: ƒ CanceledError(message)
__CANCEL__: true
[[Prototype]]: Error
Please suggest how to avoid the above error and get axios to work properly.
I also encountered the same issue and I thought that there was some flaw in my logic which caused the component to be mounted twice. After doing some digging I found that react apparently added this feature with with the new version 18 in StrictMode where useEffect was being run twice. Here's a link to the article clearly explaining this new behaviour.
One way you could solve this problem is by removing StrictMode from your application (Temporary Solution)
Another way is by using useRef hook to store some piece of state which is updated when your application is mounted the second time.
// CODE BEFORE USE EFFECT
const effectRun = useRef(false);
useEffect(() => {
let isMounted = true;
const controller = new AbortController();
const signal = controller.signal;
const getUserProfile = async () => {
try {
const response = await axiosPrivate.get('/api/identity/profile', {
signal,
});
console.log(response.data);
isMounted && setUser(response.data.user);
} catch (error) {
console.log(error);
router.push({
pathname: '/seller/auth/login',
query: { from: router.pathname },
});
}
};
// Check if useEffect has run the first time
if (effectRun.current) {
getUserProfile();
}
return () => {
isMounted = false;
controller.abort();
effectRun.current = true; // update the value of effectRun to true
};
}, []);
// CODE AFTER USE EFFECT
Found the solution from this YouTube video.
I, too, encountered this issue. What made it worse is that axios doesn't provide an HTTP status code when the request has been canceled, although you do get error.code === "ERR_CANCELED". I solved it by handling the abort within the axios interceptor:
axiosInstance.interceptors.response.use(
(response) => response,
(error) => {
if (error.code === "ERR_CANCELED") {
// aborted in useEffect cleanup
return Promise.resolve({status: 499})
}
return Promise.reject((error.response && error.response.data) || 'Error')
}
);
As you can see, I ensure that the error response in the case of an abort supplies a status code of 499.
I faced the same problem in similar project, lets start by understanding first the root cause of that problem.
in react 18 the try to make us convenient to the idea of mounting and unmounting components twice for future features that the are preparing, the the useEffect hook now is mounted first time then unmounted the mounted finally.
so they need from us adapt our projects to the idea of mount and unmount of components twice
so you have two ways, adapting these changes and try to adapt your code to accept mounting twice, or making some turn around code to overcome mounting twice, and I would prefer the first one.
here in your code after first mount you aborted your API request in clean up function, so when the component dismount and remount again it face an error when try to run previously aborted request, so it throw exception, that's what happens
1st solution (adapting to react changing):
return () => {
isMounted = false
isMounted && controller.abort()
}
so in above code we will abort controller once only when isMounted is true, and thats will solve your problem
2nd solution (turn around to react changing):
by using useRef hook and asign it to a variable and update its boolean value after excuting the whole code only one time.
const runOnce = useRef(true)
useEffect(()=>{
if(runOnce.current){
//requesting from API
return()=>{
runOnce.current = false
}
}
},[])
3rd solution (turn around to react changing):
remove React.StrictMode from index.js file

Handling axios errors in child components?

My React app has an api client component which handles axios calls to the back end. So, in the api component I have:
async function getData(path: string, params?: any) {
const object: AxiosRequestConfig = {
...obj,
method: 'GET',
headers: {
...obj.headers,
},
params,
};
const response: AxiosResponse = await axios.get(
`${baseUrl}${path}`,
object
);
return response;
});
}
(obj contains the headers and is defined earlier in the file; baseUrl is a constant which I import).
So, if I have a useEffect to retrieve data from the endpoint '/user/{userId}' whenever the state variable userId changes, I do this:
React.useEffect(() => {
const controller = new AbortController();
const getData = async () => {
try {
const url = `user/${userId}`;
let res = await Client.getData(url, {
signal: controller.signal,
});
... Do things with results ...
} catch (e) {
// Show error
if (!controller.signal.aborted) console.log('Error: ', e);
}
};
getData();
return () => {
controller.abort();
};
}, [state.userId]);
I'm just a bit confused about how errors will be handled in this code. So, if there's an error when the axios call is made (eg no network connection, the endpoint is wrong, or the user isn't found or whatever) will the catch block get called in the getData function? Or do I need a try...catch in the api component too?

Cancel Axios request in useEffect not running

I am attempting to cancel an axios request, but I am only having partial success. I have this function that returns a Promise when I make a GET request:
const getCards = (token) => {
const URL = isDev
? "https://cors-anywhere.herokuapp.com/https://privacy.com/api/v1/card"
: "https://privacy.com/api/v1/card";
const config = {
headers: {
Authorization: `Bearer ${token}`,
},
cancelToken: source.token,
};
return axios.get(URL, config);
};
I call this function inside updateCards() which looks like this:
const updateCards = async () => {
console.log("Updating Cards");
setCards([]);
setStarted(true);
setLoading(true);
let response = await getCards(token).catch((thrown) => {
if (axios.isCancel(thrown)) {
console.error("[UpdateCards]", thrown.message);
}
});
/**
* response is undefined when we cancel the request on unmount
*/
if (typeof response === "undefined") return console.log("Undefined");
console.log("Got Response from UpdateCards", response);
setLoading(false);
};
I cancel my request in the useEffect hook as so:
useEffect(() => {
return () => {
source.cancel()
}
}, [])
And I have setup my CancelToken under my state declaration like this:
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
My issue is that, if I call my updateCards() function inside of the useEffect(), I can cancel it just fine, but if I use a button to call that same function the cancel is not ran. I have looked everywhere and the only solution I have found is that I have to call my requests within the useEffect() hook, but that is not something I want to do.Where do I go from here?
Here are the resources I have looked at:
https://github.com/axios/axios#cancellation
https://medium.com/#selvaganesh93/how-to-clean-up-subscriptions-in-react-components-using-abortcontroller-72335f19b6f7
Cant cancel Axios post request via CancelToken
To have a place to store a variable that behaves like an instance variable of the component, you can use useRef. It's a container for anything you want. You can store the CancelToken in there:
function Privacy(props) {
const source = useRef(null);
function getSource() {
if (source.current == null) {
const CancelToken = axios.CancelToken;
source.current = CancelToken.source();
}
return source.current;
}
useEffect(() => {
return () => {
if (source.current != null) source.current.cancel();
}
}, [])
// call `getSource()` wherever you need the Axios source
}

Resources