问题描述:
项目中,在网络请求没有返回之前就跳转到另外的页面,结果发现控制台报错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.
项目中的请求写法类似下面这样
useEffect(() => {
const reqData = async() => {
try {
const res: any = await axios('xxxxx');
setData(res);
} catch(err) {
console.log(err);
}
}
reqData();
}, []);
原因分析:
报错告诉我们无法对已卸载的组件执行状态更新,它表示应用程序中存在内存泄漏。
因为在请求还没返回之前,用户进行了页面跳转,跳转之后当前组件被卸载。而在卸载之后,请求返回,执行await
之后的代码,对组件执行状态更新,可是此时组件已经被卸载,所以才会导致报错。
解决方案:
查看了react文档之后,知道可以通过 effect
内部的局部变量来处理无序的响应:
useEffect(() => {
let ignore = false;
const reqData = async() => {
try {
const res: any = await axios('xxxxx');
if (!ignore) { // 判断当前组件有没有被卸载
setData(res);
}
} catch(err) {
console.log(err);
}
}
reqData();
return () => { ignore = true };
}, []);
这样在组件卸载后就不会执行状态更新了
上面的方法适用于只有初始化时才会发起请求更新状态的组件。但是如果有的组件是在触发某些事件之后才发起请求更新状态的话,就需要换一个方式来防止内存泄露了。
可以通过useRef
新建一个ref
对象,用来记录当前组件有没有被卸载,在请求成功后,更新状态前判断一下,即可防止内存泄露。
const App = () => {
const [data, setData] = useState([]);
const isUnmounted = useRef(false);
useEffect(() => {
return () => {
isUnmounted.current = true;
}
}, [])
const handleClick = async() => {
const res: any = await axios('xxxx');
if (!isUnmounted.current) { // 判断当前组件有没有被卸载
setData(res);
}
}
return (
<>
<button onClick={handleClick}>点击获取数据</button>
...
</>
)
}