Коли ви запускаєте обіцянку, може пройти кілька секунд, перш ніж вона вирішиться, і до того часу користувач міг перейти до іншого місця у вашій програмі. Отже, коли 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
який поводиться як статична властивість класу, що означає, що він не робить компонент повторного відображення, коли його значення змінюється:
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