Когда вы запускаете Promise, может пройти несколько секунд, прежде чем оно разрешится, и к тому времени пользователь, возможно, перейдет в другое место в вашем приложении. Поэтому, когда Promise resolves setState
выполняется на несмонтированном компоненте, вы получаете сообщение об ошибке - как и в вашем случае. Это также может вызвать утечку памяти.
Вот почему лучше всего вынести часть асинхронной логики из компонентов.
В противном случае вам нужно будет как-то отменить свое обещание . В качестве альтернативы - в крайнем случае (это антипаттерн) - вы можете сохранить переменную, чтобы проверять, смонтирован ли компонент:
componentDidMount(){
this.mounted = true;
this.props.fetchData().then((response) => {
if(this.mounted) {
this.setState({ data: response })
}
})
}
componentWillUnmount(){
this.mounted = false;
}
Подчеркну еще раз - это антипаттерн, но в вашем случае может быть достаточно (как и в случае с Formik
реализацией).
Аналогичное обсуждение на GitHub
РЕДАКТИРОВАТЬ:
Вероятно, вот как бы я решил ту же проблему (не имея ничего, кроме React) с помощью хуков :
ВАРИАНТ А:
import React, { useState, useEffect } from "react";
export default function Page() {
const value = usePromise("https://something.com/api/");
return (
<p>{value ? value : "fetching data..."}</p>
);
}
function usePromise(url) {
const [value, setState] = useState(null);
useEffect(() => {
let isMounted = true;
request.get(url)
.then(result => {
if (isMounted) {
setState(result);
}
});
return () => {
isMounted = false;
};
}, []);
return value;
}
ВАРИАНТ B: В качестве альтернативы useRef
which ведет себя как статическое свойство класса, что означает, что он не выполняет повторную визуализацию компонента при изменении его значения:
function usePromise2(url) {
const isMounted = React.useRef(true)
const [value, setState] = useState(null);
useEffect(() => {
return () => {
isMounted.current = false;
};
}, []);
useEffect(() => {
request.get(url)
.then(result => {
if (isMounted.current) {
setState(result);
}
});
}, []);
return value;
}
function useIsMounted() {
const isMounted = React.useRef(true)
useEffect(() => {
return () => {
isMounted.current = false;
};
}, []);
return isMounted;
}
Пример: https://codesandbox.io/s/86n1wq2z8