反应钩。无法在已卸载的组件上执行React状态更新

RTW*_*RTW 8 reactjs react-hooks

我收到此错误:

无法在已卸载的组件上执行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
}
Run Code Online (Sandbox Code Playgroud)

Sam*_*ant 10

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

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>;
}
Run Code Online (Sandbox Code Playgroud)

这是另一种具有useRef

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>;
}
Run Code Online (Sandbox Code Playgroud)

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


小智 7

TL; 博士

这是一个CodeSandBox示例

其他答案当然有效,我只是想分享我想出的解决方案。我构建了这个钩子,它的工作原理就像 React 的 useState,但只有在组件被挂载时才会 setState。我发现它更优雅,因为您不必在组件中使用 isMounted 变量!

安装 :

npm install use-state-if-mounted
Run Code Online (Sandbox Code Playgroud)

用法 :

npm install use-state-if-mounted
Run Code Online (Sandbox Code Playgroud)

你可以在钩子的npm 页面上找到更高级的文档。

  • 它不起作用,来自 npm 站点:这个“解决方案”并不能避免泄漏。即使 AbortController 似乎也不是解决内存泄漏的灵丹妙药。 (2认同)

小智 5

如果您从 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;
    };
}, []);
Run Code Online (Sandbox Code Playgroud)