You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>
    );
}

核心要点

  1. 函数式更新状态:setLogs(prevLogs => [newLog, ...prevLogs])直接获取当前最新的prevLogs,彻底避免闭包捕获旧状态的问题。
  2. useCallback稳定函数引用:包裹后的traitementDataMQTT只有在依赖变化时才会重新创建,确保useEffect不会因为函数引用变化而反复执行Socket连接。

内容的提问来源于stack exchange,提问作者ypelissier

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 03:45:05