问题描述:

项目中,在网络请求没有返回之前就跳转到另外的页面,结果发现控制台报错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>
      ...
    </>
  )
}
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐