我遇到了一个奇怪的问题。
我有一个包含以下代码的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]);
...
}