React中setInterval应放在哪里?Header组件内使用遇内存泄漏如何解决
问题根因&解决方案
现有代码的核心错误
- 第一,
useEffect不能直接传入async回调:React要求useEffect的返回值只能是清理函数,而async函数默认返回Promise,会导致你写的clearInterval清理逻辑完全不生效。这就是你两个异常的核心来源:- 组件卸载后定时器还在运行,请求回来后调用
setData就触发了「不能给已卸载组件更新状态」的内存泄漏警告 - 每次Header组件重新挂载(比如页面跳转回来)都会新建一个定时器,旧的定时器没清,运行的定时器越来越多,自然就出现几秒内多次触发的问题
- 组件卸载后定时器还在运行,请求回来后调用
- 第二,没有处理请求竞态和组件卸载后的请求取消:就算定时器清了,已经发出去的请求回来后还是会尝试更新状态,同样会触发警告
关于定时器放置位置的问题
- 如果你的Header是全局常驻组件(所有页面都展示,不会随路由切换卸载),那把定时器放在Header内部是完全合理的,不需要挪到App.js
- 如果Header会随路由切换挂载/卸载,那更推荐把定时器放到App.js这类全局不会卸载的组件里,数据可以用Context下发,也可以根据你的项目技术栈用Redux、Zustand这类状态管理工具存储,Header直接取全局状态即可
修正后的代码示例
const [data, setData] = useState([]); useEffect(() => { // 标记组件是否已卸载 let isMounted = true; const interval = setInterval(async () => { if(!localStorage.getItem("loggedInUser")) return; console.log("Logged in user:", new Date()); try{ const res = await getData(); // 只有组件还挂载、有返回数据才更新状态 if(isMounted && res){ setData(res); } console.log("data", res); } catch(err){ console.log("err", err); } }, 5000); // 清理逻辑 return () => { isMounted = false; clearInterval(interval); }; }, []);
小提示:如果担心请求耗时超过定时间隔导致请求堆叠,可以把setInterval换成递归setTimeout,保证上一次请求处理完成后再开始计时下一次请求,更稳妥。
内容的提问来源于stack exchange,提问作者oderfla
相关产品推荐
相关产品推荐

