React使用EventSource监听事件后useState更新出现累积重复日志如何解决
问题产生原因
- 副作用未限制执行时机且未清理:原代码的
useEffect没有传入第二个依赖参数,这会导致每次组件重渲染时都会重新执行useEffect逻辑,重复调用connect新建EventSource连接、注册新的heartBeat事件监听器。同时旧的连接没有被关闭、旧的监听器没有被移除,每次heartBeat事件触发时,历史累计注册的所有监听器都会同时执行,直接表现就是日志数量逐次递增。 - 闭包捕获旧值问题:
handleHeartBeat定义在组件内部,每一轮渲染都会生成新的函数实例,闭包会捕获当前渲染周期的x值。不同轮次注册的监听器拿到的都是注册时的旧x值,因此输出的数值序列会是历史累积的旧值组合。
修复方案
核心是限制连接仅初始化一次,做好副作用清理,同时避免闭包旧值问题:
- 给
useEffect添加空依赖数组,保证仅在组件挂载时初始化一次EventSource连接 - 在
useEffect的清理函数中添加连接关闭、监听器移除逻辑,避免内存泄漏和重复监听 - 若需要打印最新的
x值,可在setX的函数式回调中打印,或单独用useEffect监听x变化打印
修复后代码示例:
import { useState, useEffect, useRef } from 'react'; export default function ComponentA(props) { const [x, setX] = useState(0); // 用useRef存储EventSource实例,跨渲染周期保持引用 const esRef = useRef(null); useEffect(() => { const connect = (url) => { esRef.current = new EventSource(url, { withCredentials: false }); esRef.current.addEventListener('heartBeat', handleHeartBeat); }; const handleHeartBeat = (event) => { setX((prevX) => { const newX = prevX + 1; // 在setX的函数式回调中打印可拿到最新的x值 const currentdate = new Date(); const datetime = `${currentdate.getHours()}:${currentdate.getMinutes()}:${currentdate.getSeconds()}`; console.log("time =", datetime + "; x =", newX); return newX; }); }; connect("http://localhost:18200/events/agg/dotusd"); // 清理函数:组件卸载时关闭连接、移除监听器 return () => { if (esRef.current) { esRef.current.removeEventListener('heartBeat', handleHeartBeat); esRef.current.close(); esRef.current = null; } }; }, []); // 空依赖数组,仅组件挂载时执行一次 return ( <div> <ComponentB key='1' x={x} /> </div> ); }
内容的提问来源于stack exchange,提问作者programista4k
相关产品推荐
相关产品推荐

