【问题标题】:React useEffect unmount protection is not working as expectedReact useEffect 卸载保护未按预期工作
【发布时间】:2021-07-27 23:39:59
【问题描述】:

我正在使用 React 在 useEffect 中进行一些异步数据获取,以处理页面刷新时的用户状态。我收到以下错误,根据谷歌结果,这似乎很常见:

警告:无法对未安装的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要解决此问题,请在 useEffect 清理函数中取消所有订阅和异步任务。

根据与此相关的所有文档,此错误是基于组件已卸载时可能更新的状态。对此建议的基本修复方法是在执行状态更新之前检查变量状态跟踪组件是否仍然挂载。

但是,我已经实施了建议的修复程序,但在页面刷新时仍然收到错误消息。非常感谢任何额外的指导。下面提供了精简的代码示例:

const [user, setUser] = React.useState(undefined);
const [isLoggedIn, setIsLoggedIn] = React.useState(false);

React.useEffect(() => {
    let mounted = true;
    
    axios.get('https://api.endpoint/session', { withCredentials: true }).then(session => {
      if(mounted) {
        setIsLoggedIn(true);
        setUser(session.data);
      }
    });

    return () => mounted = false;
  }, [])

【问题讨论】:

标签: reactjs asynchronous axios use-effect


【解决方案1】:

这个代码片段对我来说很好用。您安装的只是一个局部变量,我没有看到您有任何代码可以在组件卸载后设置状态。您确定这是问题所在吗?

【讨论】:

  • 是的,我确定。这是我代码中目前我利用 useEffect 的唯一位置,如果我删除以下 2 个调用,错误就会消失: setIsLoggedIn(true); setUser(session.data);
  • 如果你能贴出完整的组件代码,也许我们可以再看看。
【解决方案2】:

我收到了关于重组代码的建议,它为我解决了这个问题:

const [user, setUser] = React.useState(undefined);
const [isLoggedIn, setIsLoggedIn] = React.useState(false);
React.useEffect(() => {
    let mounted = true;
    const fetchSession = async () => {
      const session = await axios.get('https://api.endpoint/session', { withCredentials: true });
      if(mounted) {
        setUser(session.data);
        setIsLoggedIn(true);
      }
    }
    fetchSession();
    return () => mounted = false;
  }, [])

与我的示例相比,我不完全理解为什么这可以解决问题,但如果其他人像我一样被卡住,这可能会对你有所帮助。

此外,windmaomao 对我的帖子的评论可能是一种更有效的方法,但在我实现他的基于钩子的版本时,这暂时可以作为一种快速修复。

【讨论】:

  • 目前尚不清楚为什么这有效,而您问题中的版本无效。这两个版本在我的本地机器上运行都没有问题。
  • 同意,当我在不了解解决问题的根本原因的情况下解决问题时,我真的很烦。我也在本地开发,所以也许正在发生一些时髦的事情。根据我的研究,似乎任何一个版本都可以正常工作,但对我来说,该示例始终在控制台中产生此警告,而这个新版本没有。
【解决方案3】:

该代码运行良好 (Live Demo):

import React from "react";
import axios from "axios";

function TestComponent(props) {
  const [user, setUser] = React.useState(undefined);
  const [isLoggedIn, setIsLoggedIn] = React.useState(false);

  React.useEffect(() => {
    let mounted = true;

    axios.get(props.url, { withCredentials: true }).then((session) => {
      if (mounted) {
        setIsLoggedIn(true);
        setUser(session.data);
      }
    });

    return () => (mounted = false);
  }, [props.url]);
  return (
    <div className="component">
      <div className="caption">useEffect demo:</div>
      <div>{JSON.stringify(user)}</div>
    </div>
  );
}

顺便说一句,您可以通过使用自定义挂钩来执行异步自动取消效果(网络请求将在卸载时中止),从而让您的生活更轻松: Live Demo

import React from "react";
import { useAsyncEffect } from "use-async-effect2";
import cpAxios from "cp-axios";

function TestComponent(props) {
  const [cancel, done, result, err] = useAsyncEffect(
    function* () {
      return (yield cpAxios(props.url).data;
    },
    { states: true, deps: [props.url] }
  );

  return (
    <div className="component">
      <div className="caption">useAsyncEffect demo:</div>
      <div>
        {done ? (err ? err.toString() : JSON.stringify(result)) : "loading..."}
      </div>
      <button className="btn btn-danger" onClick={cancel} disabled={done}>
        Cancel async effect
      </button>
    </div>
  );
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-04
    相关资源
    最近更新 更多