React组件里用定时器导致内存泄漏怎么优化?
我在写一个实时刷新数据的React组件,发现组件卸载后计数器还在跑,内存占用越来越高。代码是这样写的:
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setCount(prev => prev + 1); // 这里感觉有问题
}, 1000);
return () => clearInterval(timer); // 清理函数好像没生效?
}, []);
return 计数:{count};
}
虽然写了清理函数,但页面切换后控制台还持续输出计数。用React DevTools发现组件卸载后,闭包里仍然保留着count状态引用。尝试过把依赖数组改成[count]但导致无限循环,该怎么正确处理闭包引用避免内存泄漏呢?
clearInterval是对的,但得确保它真能执行到。首先你写的清理函数其实没问题,
useEffect返回的函数会在组件卸载时调用,清除定时器。但如果你在开发环境用的是 React 18 的严格模式,useEffect会故意执行两次(mount + unmount + mount),这可能会让你误以为清理没生效——其实是清了又重新开了一个。你可以先关掉严格模式验证一下真实行为。至于你说的“闭包保留 count 引用”,这不是内存泄漏的根本原因。真正的问题是:只要定时器还在跑,回调函数就一直被持有,里面的
setCount就不会释放,进而持有了组件作用域,导致本该回收的组件实例无法 GC。解决方法很简单:
用
useRef来标记组件是否挂载,避免在组件卸载后还去更新状态。虽然定时器清了,但在最后一次回调和清理之间可能仍有微小的时间窗口,触发setCount导致报错或内存占用。修改后的代码:
这样即使定时器回调延迟执行,也会因为
mounted.current为 false 而跳过setCount,避免无效状态更新。另外,如果你是在做实时数据刷新,比如轮询 API 调用,建议别用
setInterval,改用递归setTimeout,防止请求堆积。例如:总结:你的清理逻辑是对的,但要防开发模式双执行、加挂载标记、必要时换成递归定时避免并发问题。这才是生产级写法。
我之前这样搞的,基本没问题了。