React Hooks中componentDidMount等效写法导致日志数组异常问题
React Hooks 单次Socket连接与日志数组更新问题
我使用React 16.14的Hooks开发,想在组件首次加载时(对应类组件componentDidMount的效果)建立单次Socket连接,但遇到两个棘手问题:
- 给
useEffect设置[]依赖(仅首次执行)时,日志数组永远只保留最后一条新元素; - 不设置依赖的话,每次组件更新都会断开并重新连接Socket。
以下是我的测试代码:
export default function ShowLog(props) { const [logs, setLogs] = useState([]); const traitementDataMQTT = async data => { console.log("Start Reception => " + DateUtilities.toISODate(data.DateHeure) + " - data.Message"); console.log(logs) let _logs = [...logs]; console.log(_logs) _logs.unshift(DateUtilities.toISODate(data.DateHeure) + " - Nouveau message"); console.log(_logs) setLogs(_logs); console.log(logs) console.log("End reception") } useEffect(() => { console.log("Connect socket"); let socket = io.connect(process.env.REACT_APP_IP_SOCKET, { secure: true }); socket.on("/NiCo/IHM/Message", data => traitementDataMQTT(data)); return () => { console.log("Close socket"); socket.close(); } } // 两种依赖写法的差异 ) // 不设依赖:每次更新都会断开重连(类似componentDidMount + componentWillUpdate) // , []) // 设为空数组:仅连接一次(类似componentDidMount),但日志数组始终只有一条元素 // 状态检查用的Effect useEffect(() => { console.log("Log changed"); console.log(logs); }, [logs]); useEffect(() => { console.log("All change"); console.log(logs); }); console.log("Test - Function"); return ( <div> <table> <tbody id="Logs"> { logs.map( (item, index) => <tr key={index}> <td> <p>{item}</p> </td> </tr>) } </tbody> </table> </div> ); }
控制台两种情况的结果如下:
问题根源
当useEffect依赖设为[]时,traitementDataMQTT函数会捕获组件首次渲染时的logs空数组。每次触发Socket回调时,都是基于这个旧的空数组创建新数组并更新状态,所以最终数组里永远只有刚添加的那条元素。而不设依赖的话,useEffect会在每次组件渲染时重新执行,导致Socket反复连接断开。
解决方案
直接修改两处关键逻辑,解决闭包和重复连接问题:
export default function ShowLog(props) { const [logs, setLogs] = useState([]); // 用useCallback包裹函数,保证引用稳定 const traitementDataMQTT = useCallback(async data => { const newLog = `${DateUtilities.toISODate(data.DateHeure)} - Nouveau message`; console.log("Start Reception =>", newLog); // 使用函数式更新,获取最新的日志状态 setLogs(prevLogs => [newLog, ...prevLogs]); console.log("End reception"); }, []); useEffect(() => { console.log("Connect socket"); const socket = io.connect(process.env.REACT_APP_IP_SOCKET, { secure: true }); socket.on("/NiCo/IHM/Message", traitementDataMQTT); return () => { console.log("Close socket"); socket.close(); } }, [traitementDataMQTT]) // 把稳定的函数引用加入依赖 // 状态检查用的Effect useEffect(() => { console.log("Log changed"); console.log(logs); }, [logs]); useEffect(() => { console.log("All change"); console.log(logs); }); console.log("Test - Function"); return ( <div> <table> <tbody id="Logs"> {logs.map((item, index) => ( <tr key={index}> <td> <p>{item}</p> </td> </tr> ))} </tbody> </table> </div> ); }
核心要点
- 函数式更新状态:
setLogs(prevLogs => [newLog, ...prevLogs])直接获取当前最新的prevLogs,彻底避免闭包捕获旧状态的问题。 - useCallback稳定函数引用:包裹后的
traitementDataMQTT只有在依赖变化时才会重新创建,确保useEffect不会因为函数引用变化而反复执行Socket连接。
内容的提问来源于stack exchange,提问作者ypelissier
相关产品推荐
相关产品推荐

