反应钩子。无法对未安装的组件执行 React 状态更新

新手上路,请多包涵

我收到此错误:

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

当开始获取数据并卸载组件时,函数正在尝试更新已卸载组件的状态。

解决这个问题的最佳方法是什么?

CodePen 示例

 default function Test() {
    const [notSeenAmount, setNotSeenAmount] = useState(false)

    useEffect(() => {
        let timer = setInterval(updateNotSeenAmount, 2000)

        return () => clearInterval(timer)
    }, [])

    async function updateNotSeenAmount() {
        let data // here i fetch data

        setNotSeenAmount(data) // here is problem. If component was unmounted, i get error.
    }

    async function anotherFunction() {
       updateNotSeenAmount() //it can trigger update too
    }

    return <button onClick={updateNotSeenAmount}>Push me</button> //update can be triggered manually
}

原文由 ZiiMakc 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 607
2 个回答

最简单的解决方案是使用局部变量来跟踪组件是否已安装。这是基于类的方法的常见模式。这是 一个使用钩子实现它的示例

 function Example() {
  const [text, setText] = React.useState("waiting...");

  React.useEffect(() => {
    let isCancelled = false;

    simulateSlowNetworkRequest().then(() => {
      if (!isCancelled) {
        setText("done!");
      }
    });

    return () => {
      isCancelled = true;
    };
  }, []);

  return <h2>{text}</h2>;
}

这是 useRef 的替代方案(见下文)。请注意,对于依赖项列表,此解决方案将不起作用。 ref 的值将在第一次渲染后保持为真。在这种情况下,第一个解决方案更合适。

 function Example() {
  const isCancelled = React.useRef(false);
  const [text, setText] = React.useState("waiting...");

  React.useEffect(() => {
    fetch();

    return () => {
      isCancelled.current = true;
    };
  }, []);

  function fetch() {
    simulateSlowNetworkRequest().then(() => {
      if (!isCancelled.current) {
        setText("done!");
      }
    });
  }

  return <h2>{text}</h2>;
}

您可以在 本文 中找到有关此模式的更多信息。这是 GitHub 上 React 项目中 的一个问题,展示了此解决方案。

原文由 Samuel Vaillant 发布,翻译遵循 CC BY-SA 4.0 许可协议

如果您从 axios(使用钩子)获取数据并且错误仍然发生,只需将 setter 包装在条件中

let isRendered = useRef(false);
useEffect(() => {
    isRendered = true;
    axios
        .get("/sample/api")
        .then(res => {
            if (isRendered) {
                setState(res.data);
            }
            return null;
        })
        .catch(err => console.log(err));
    return () => {
        isRendered = false;
    };
}, []);

原文由 Drew Cordano 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进