当你发出promise 时,可能需要几秒钟时间才能解决问题,到那时,用户可能已经导航到了你应用程序中的另一个地方.所以,当Promise resolves setState
在未安装的组件上执行时,您会得到一个错误——就像您的情况一样.这也可能导致内存泄漏.
That's why it is best to move some of your asynchronous logic out of components.
否则,你将需要以某种方式cancel your Promise.或者,作为最后手段(这是一种反模式),您可以保留一个变量来判断组件是否仍在安装:
componentDidMount(){
this.mounted = true;
this.props.fetchData().then((response) => {
if(this.mounted) {
this.setState({ data: response })
}
})
}
componentWillUnmount(){
this.mounted = false;
}
我将再次强调这一点——这is an antipattern个,但在您的情况下可能就足够了(就像Formik
实现一样).
关于GitHub的类似讨论
EDIT:
这可能就是我如何用Hooks解决同样的问题(除了react 之外什么都没有):
OPTION A:
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; // track whether component is mounted
request.get(url)
.then(result => {
if (isMounted) {
setState(result);
}
});
return () => {
// clean up
isMounted = false;
};
}, []); // only on "didMount"
return value;
}
OPTION 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;
}
// or extract it to custom hook:
function useIsMounted() {
const isMounted = React.useRef(true)
useEffect(() => {
return () => {
isMounted.current = false;
};
}, []);
return isMounted; // returning "isMounted.current" wouldn't work because we would return unmutable primitive
}
示例:https://codesandbox.io/s/86n1wq2z8