У меня есть следующий код
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
);
};
Я получаю следующую ошибку: 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.
Также просто для уточнения, что тег Call
— это метод, импортированный axios
, который я использую для обработчика ошибок URL и т. д. Проблема, как предполагает ошибка, находится в разделе useEffect
.
@NemanjaLazarevic У меня есть только один useEffect в моем проекте, так что это должно быть так.
Вы можете опубликовать код, в котором вы визуализируете компонент <Companies />
. Возможно, у вас есть какая-то логика, которая преждевременно размонтирует ваш компонент <Companies />
.
@NemanjaLazarevic Есть еще один компонент под названием LayoutCompanies, который возвращает Box из MatUI. <Box> <Companies /> </Box>
Очевидно, вы не ожидаете, что этот компонент размонтируется (что предполагает ошибка). Чтобы подтвердить, что это так, вы можете добавить return () => {console.info("Component Unmounded")}
внизу вашего useEffect
. Если после обновления вы видите этот журнал, значит, вам нужно искать виновника где-то вверху в дереве компонентов.
Ваша проблема в том, что вы вызываете setCompaniesData
, когда компонент уже размонтирован.
Таким образом, вы можете попробовать использовать токен отмены, чтобы отменить запрос axios.
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();
}
};
Второй вариант — отслеживать смонтированное состояние компонента и вызывать setState только в том случае, если компонент все еще смонтирован. Посмотрите это видео здесь: https://thewikihow.com/video__TleXX0mxaY&ab_channel=LeighHalliday
Проблема, с которой вы столкнулись, заключается в том, что вы создаете запрос на сервер, в то время как пользователь/вы размонтируете (измените представление/перерисуете) компонент, ожидающий данных с сервера.
Отмена запросов ajax - это хорошая практика, а не просто реакция.
Axios использует токены отмены для отмены — отмена
По моему опыту лучше обернуть целые аксиомы в свой собственный код. И обрабатывать отмену там. (что-то вроде api.get(/route
); api.cancel(/route
))
Надеюсь, поможет
Вот общий пример json axios fetch с отменой асинхронной задачи (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.info("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.info("unmount");
promise.cancel(); // cancel async task
};
}, [props.url]);
return <p>{text}</p>;
}
Когда именно вы получаете это сообщение об ошибке?