Reputation: 116
I have the following code
const Companies = () => {
const [company, setCompany] = useState(plainCompanyObj);
const [companiesData, setCompaniesData] = useState([]);
useEffect(() => {
Call<any, any>({
url:
_baseApiUrl +
"-------api goes here --------",
method: "GET",
data: null,
success: (companies) => {
setCompaniesData(companies.companies);
},
authorization: sessionStorage.getItem("accessToken"),
});
}, []);
return (
//return JSX
);
};
I get the following error: Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
Also just to clarify the Call
tag is a method imported by axios
that I use for URL error handler etc. The problem as the error suggests is in the useEffect
section.
Upvotes: 1
Views: 1726
Reputation: 1597
Here is a generic json axios fetch example with async task cancellation (Live demo):
import React, { useEffect, useState } from "react";
import { CPromise, CanceledError } from "c-promise2";
import cpAxios from "cp-axios";
function MyComponent(props) {
const [text, setText] = useState("fetching...");
useEffect(() => {
console.log("mount");
const promise = CPromise.from(function* () {
try {
const response = yield cpAxios(props.url);
setText(`Success: ${JSON.stringify(response.data)}`);
} catch (err) {
console.warn(err);
CanceledError.rethrow(err); //passthrough
// handle other errors than CanceledError
setText(`Failed: ${err}`);
}
});
return () => {
console.log("unmount");
promise.cancel(); // cancel async task
};
}, [props.url]);
return <p>{text}</p>;
}
Upvotes: 1
Reputation: 732
The problem you are having is that, you create request to server meanwhile user/you unmount (change view/rerender) the component which is waiting for data from server.
Canceling ajax requests is good practice, its not just react thing.
Axios uses cancel tokens for canceling - canceling
From my experience it is better to wrap whole axios into your own code. And handle cancelation there. (something like api.get(/route
); api.cancel(/route
))
Hope it helps
Upvotes: 1
Reputation: 2302
Your problem is that you are calling setCompaniesData
when component was already unmounted.
So you can try using cancel token, to cancel axios request.
https://github.com/axios/axios#cancellation
const Companies = () => {
const [company, setCompany] = useState(plainCompanyObj);
const [companiesData, setCompaniesData] = useState([]);
useEffect(() => {
const cancelTokenSource = axios.CancelToken.source();
Call<any, any>({
url:
_baseApiUrl +
"-------api goes here --------",
method: "GET",
data: null,
cancelToken: cancelTokenSource.token
success: (companies) => {
setCompaniesData(companies.companies);
},
authorization: sessionStorage.getItem("accessToken"),
});
}, []);
return () => {
cancelTokenSource.cancel();
}
};
Second option is to keep track of mounted state of the component, and only call setState if the component is still mounted. Check this video here: https://www.youtube.com/watch?v=_TleXX0mxaY&ab_channel=LeighHalliday
Upvotes: 1