При выборке данных в React 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. in ParentComponent
является обычным предупреждением. Я прочитал несколько сообщений и предложений о том, как с этим справиться, и в настоящее время ни один из них не работает.
Для этого у нас есть функция useAxiosApi
, которая извлекает данные асинхронно, и ParentComponent
, который является компонентом, который использует useAxiosApi()
и ему нужны данные. ParentComponent
- компонент, который размонтируется / упоминается в предупреждениях.
Родительский компонент
import useAxiosApi...
function ParentComponent({ info }) {
const dataConfig = { season: info.season, scope: info.scope };
const [data, isLoading1, isError1] = useAxiosApi('this-endpoint', [], dataConfig);
return (
{isLoading && <p>We are loading...</p>}
{!isLoading &&
... use the data to render something...
}
)
}
useAxiosApi
import axios from 'axios';
import { useState } from 'react';
import useDeepCompareEffect from 'use-deep-compare-effect';
const resources = {};
const useAxiosApi = (endpoint, initialValue, config) => {
// Set Data-Fetching State
const [data, setData] = useState(initialValue);
const [isLoading, setIsLoading] = useState(true);
const [isError, setIsError] = useState(false);
// Use in lieu of useEffect
useDeepCompareEffect(() => {
// Token/Source should be created before "fetchData"
let source = axios.CancelToken.source();
let isMounted = true;
// Create Function that makes Axios requests
const fetchData = async () => {
// For Live Search on keystroke, Save Fetches and Skip Fetch if Already Made
if (endpoint === 'liveSearch' && resources[config.searchText]) {
return [resources[config.searchText], false, false];
}
// Otherwise, Continue Forward
setIsError(false);
setIsLoading(true);
try {
const url = createUrl(endpoint, config);
const result = await axios.get(url, { cancelToken: source.token });
console.log('isMounted: ', isMounted);
if (isMounted) {
setData(result.data);
}
// If LiveSearch, store the response to "resources"
if (endpoint === 'liveSearch') {
resources[config.searchText] = result.data;
}
} catch (error) {
setIsError(true);
} finally {
setIsLoading(false);
}
};
// Call Function
fetchData();
// Cancel Request if needed in cleanup function
return () => {
console.log('Unmount or New Search? About to call source.cancel()');
isMounted = false; // is this doing its job?
source.cancel();
};
}, [endpoint, config]);
// Return as length-3 array
return [data, isLoading, isError];
};
export default useAxiosApi;
createUrl
- это просто функция, которая принимает endpoint
и dataConfig
и создает URL-адрес, из которого будет извлекаться ax ios. Обратите внимание, что наши cancelTokens, похоже, работают вместе с поиском в реальном времени, так как новые поисковые запросы отменяют старые поисковые запросы, а сохранение результатов данных в resources
для одной указанной c конечной точки liveSearch
также работает.
Однако наша проблема в том, что при быстром отключении ParentComponent
до завершения выборки данных мы все равно получаем предупреждение Cant perform a React state update
. Я проверил console.logs()
, и console.log('isMounted: ', isMounted)
is всегда возвращает true , даже если мы быстро отключим компонент после его монтирования / до завершения выборки данных.
Мы ' Мы в недоумении по этому поводу, так как использование переменной isMounted
- это способ, которым я видел решение этой проблемы раньше. Возможно, проблема связана с перехватом useDeepCompareEffect
? Или, может быть, мы упускаем что-то еще. Мы будем очень благодарны за любую помощь или мысли по этому поводу.
Изменить: Мы также пытались создать переменную isMounted
изнутри ParentComponent
и передать ее в качестве параметра в useAxiosApi
, но у нас это тоже не сработало ... В общем, было бы намного лучше, если бы мы могли обработать это предупреждение с помощью обновления нашей функции useAxiosApi
, в отличие от ParentComponent
.
Edit2: Похоже, что cancelToken
работает только тогда, когда дублирующийся вызов API запускается для той же конечной точки. Это хорошо для нашего liveSearch, однако означает, что все другие выборки не отменяются.