我在一个 React 组件里用了 setInterval 轮询数据,离开页面后发现控制台还在打印日志,怀疑定时器没清理。试过在 useEffect 返回函数里清除,但有时候还是漏掉,这算内存泄漏吗?
这是我的代码:
useEffect(() => {
const timer = setInterval(() => {
console.log('fetching...');
fetchData();
}, 5000);
return () => clearInterval(timer);
}, []);
你的代码结构是正确的,useEffect 返回清理函数是标准做法。但有时候定时器还在跑,可能是下面这几个原因:
原因一:定时器 ID 没正确保存
如果你的代码稍微复杂一点,比如这样:
看起来没问题,但如果 setInterval 执行过程中报错,timer 根本没拿到,后续清理时 clearInterval(timer) 实际上执行的是 clearInterval(undefined),不会有任何效果。
原因二:fetchData 内部又开了定时器
检查一下 fetchData 函数内部有没有再开 setTimeout 或 setInterval,很多人是这里漏掉的。
原因三:React 18 严格模式搞的鬼
如果你在开发环境用了 React 18 的 StrictMode,组件会先挂载再卸载再挂载一次,这是故意的,为了检测副作用问题。这意味着 useEffect 会执行两次,清理函数也会执行两次。虽然这不应该导致定时器泄漏,但如果你依赖某个全局状态,可能会有些诡异。
推荐写法,加个保险:
如果还是有问题,试试这个调试方法:
在清理函数里加日志,看它到底有没有执行:
如果控制台看到 "组件卸载,清理定时器" 但日志还在打印,那问题就在 fetchData 里面,它可能又起了新的定时器。
关于内存泄漏
你这个情况算不算内存泄漏要看具体情况。定时器本身占用的内存很小,主要问题是:
1. 定时器回调如果引用了组件的状态或函数,组件虽然卸载了,但这些闭包还活着
2. 如果 fetchData 还在发请求,请求回来后setState 到一个已经卸载的组件上,会报 warning
这种情况更准确的说法是"僵尸效应"或者"陈旧的闭包",不一定算传统意义上的内存泄漏,但后果类似。
你先按上面的调试方法看看清理函数有没有执行,以及 fetchData 里面有没有藏定时器。