无法对已卸载的组件执行React状态更新-即使清除间隔

时间:2020-05-18 21:22:13

标签: reactjs react-hooks

我面临一个奇怪的问题。

我有一个FCM Token组件,其中包含以下代码:

Users

即使我清除了useEffect的返回函数中的间隔,我也在控制台中收到了此消息(指向notification组件):

Family member User

返回Message而不是const Message = (props) => { const [timeout, setTimerTimeout] = useState(null); const [someVar, setSomeVar] = useState(null); useEffect(() => { setTimerTimeout(prevTimeout => { if (prevTimeout) { clearInterval(prevTimeout); } return setInterval(someFunc, 1000) }); }, [someVar]); useEffect(() => { return () => { clearInterval(timeout); }; }, []); ... } 时,警告消失了(但是这当然不是我想要的,我只是知道间隔导致了问题)。

我不知道我在做什么错以及如何消除它。

有什么主意吗?谢谢!

3 个答案:

答案 0 :(得分:2)

您的第二个useEffect仅在组件首次渲染时创建一次,因此它对timeout的值为null。因此它将清除null。

尽管您不需要两种效果,而只需要一种。您可以修改第一个效果以包括一个拆解功能,而无需保存计时器ID来声明:

useEffect(() => {
  let id = setInterval(someFunc, 1000);
  return () => clearInterval(id);
}, [someVar]);

答案 1 :(得分:1)

如果组件在返回之前就已卸载,则可以使用保护变量来取消异步操作(在这种情况下为计时器,在其他情况下为获取请求)。

const Message = (props) => {

  const [startInterval, setStartInterval] = useState(null);  

  useEffect(() => {

    let cancel = false;

    const intervalID = setInterval(() => { // run every second
      if (!cancel) {                       // but don't run if component unmounts
        someFunc();
      }
    }, 1000);

    return () => {               // runs when component unmounts
      cancel = true;             // stop an already-running interval
      clearInterval(intervalID); // stop the interval generator
    });

  }, [startInterval]);

  ...

}

如果您使用setSomeVar来允许用户手动停止计时器(例如发生点击事件)

const Message = (props) => {

  const [startInterval, setStartInterval] = useState(null);  
  const [stopInterval, setStopInterval] = useState(null);  

  useEffect(() => {

    let cancel = false;

    let intervalID;
    if (stopInterval && intervalID) {

      clearInterval(intervalID); // manually stop the interval generator

    } else {

      intervalID = setInterval(() => { // run every second
        if (!cancel) {                 // but don't run if component unmounts
          someFunc();
        }
      }, 1000);
    }

    return () => {               // runs when component unmounts
      cancel = true;             // stop an already-running interval
      clearInterval(intervalID); // stop the interval generator
    });

  }, [startInterval, stopInterval]);

  ...

}

答案 2 :(得分:0)

useEffect钩子的清理功能应该是建立效果的同一调用的一部分。

尝试一下:

useEffect(() => {
    () => setTimerTimeout(prevTimeout => {
        if (prevTimeout) {
            clearInterval(prevTimeout);
        }
        return setInterval(someFunc, 1000)
    });
    return () => clearInterval(timeout);
}, [someVar]);

https://reactjs.org/docs/hooks-effect.html#effects-with-cleanup