防止在异步事件处理程序期间更新卸载的组件

时间:2020-11-12 22:05:41

标签: reactjs

我有一个异步事件处理程序,该事件处理程序删除了一个组件,但是此组件正在使用状态来监视事件处理程序的执行状态。事件处理程序是从远程数据库删除项目的模拟。

问题在于,成功删除后,组件将被卸载,因此最终状态更新(表明已完成删除)将触发错误“无法对已卸载的组件执行React状态更新”。

我知道这是经常发生的经典问题,我想知道什么是解决该问题的最佳方法。

沙箱: Edit SO-unruffled-bassi-2odi4

完整代码供参考:

import React from "react";

export default function App() {
  const [fruits, setFruits] = React.useState(["apple", "pear", "peach"]);

  return (
    <ul>
      {fruits.map((fruit) => (
        <Row key={fruit} fruit={fruit} setFruits={setFruits} />
      ))}
    </ul>
  );
}

function Row({ fruit, setFruits }) {
  const [isDeleting, setIsDeleting] = React.useState(false);

  const handleDelete = async () => {
    setIsDeleting(true);
    try {
      await deleteFruit(fruit, setFruits);
    } catch (error) {
      console.log("An error occured");
    }
    setIsDeleting(false);
  };

  return (
    <li>
      {fruit}
      <button onClick={handleDelete} disabled={isDeleting}>
        X
      </button>
    </li>
  );
}

async function deleteFruit(fruitToDelete, setFruits) {
  // mock remote DB
  return new Promise((resolve) => {
    setTimeout(() => {
      setFruits((fruits) => fruits.filter((f) => f !== fruitToDelete));
      resolve();
    }, 1000);
  });
}

我试图通过记录是否通过useRef和useEffect装入组件来防止该问题。它可以工作,但是我发现它不容易阅读。有没有更明确的方法来实现这种行为?

在组件Row的渲染函数中:

const refIsMounted = React.useRef(true);

React.useEffect(() => {
  refIsMounted.current = true;
  return () => {
    refIsMounted.current = false;
  };
}, []);

以及异步事件处理程序:

if (refIsMounted.current) {
  setIsDeleting(false);
}

2 个答案:

答案 0 :(得分:0)

基于useEffect文档,您可以返回清理功能。

通常,效果会创建需要在使用前清理的资源 组件离开屏幕,例如订阅或计时器ID。去做 这样,传递给useEffect的函数可能会返回清理函数。

所以您的代码将如下所示:

useEffect(()=>{
      return ()=>{
            setIsDeleting(false);}
},[])

答案 1 :(得分:0)

我意识到问题所在:触发异步事件处理程序的组件的生命周期与此异步事件处理程序的执行时间无关

所以解决方案是:

  • 将处理程序修改的状态放在一个组件中,我们知道该组件将比处理程序更有效,无论是在组件层次结构中还是在 Redux 中。
  • 使用上面描述的 useRef 技巧来检查触发组件是否仍然存在

这里我解除了父组件中的状态: Edit SO-unruffled-bassi-2odi4 (forked)