无法对未挂载的组件执行 React 状态更新 - 即使在清除间隔时也是如此



我遇到了一个奇怪的问题。

我有一个包含以下代码的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);
};
}, []);
...
}

即使我正在清除 useImpact 的返回函数中的间隔,我也会在控制台中收到以下消息(指向Message组件(:

Warning: Can't perform a React state update on an unmounted component. 
This is a no-op, but it indicates a memory leak in your application. 
To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

当返回null而不是setInterval(someFunc, 1000)时,警告消失了(但当然这不是我想要的,我只知道间隔导致了问题(。

我不知道我做错了什么以及如何摆脱它。

知道吗?谢谢!

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

不过,你不需要两种效果,你只需要一种。您可以修改第一个效果以包含拆解功能,并且不需要保存计时器 ID 来声明:

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

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

您可以使用保护变量,如果组件在返回之前被卸载,则取消异步操作(在本例中为计时器,或在另一种情况下为 fetch 请求(。

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]);
...
}

相关内容

  • 没有找到相关文章

最新更新