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

React中useEffect空依赖仍触发两次渲染及重复请求问题

useState为何触发两次渲染?

enter image description here

我在useEffect的末尾使用了[]空依赖数组,按预期应该只执行一次,但数据请求却执行了两次,导致组件重复渲染。相关代码如下:

useEffect(() => {
    const socket = io("http://localhost:3001");
    let public_key;
    if (roomID.length == 0) {
      axios
        .post(
          `${domain}/chat/publickey?is_update_key=false`,
          {},
          { withCredentials: true }
        )
        .then((res) => {
          console.log(res);
          public_key = res.data.detail;
          console.log(public_key);
          socket.emit("admin_room", public_key, socket.id);
          axios
            .get(`${domain}/chat/admin/${public_key}?page=1&items_per_page=5`, {
              withCredentials: true,
            })
            .then((res) => {
              setRoomID((prev) => [...prev, res.data]);
            });
        })
        .catch((err) => {
          console.log(err);
        });
    }

    socket.on("customer_message", (data) => {...my other socket io code
    
    return () => {
      socket.disconnect();
    };
  }, []);

问题原因及解决办法

  1. React 18 Strict Mode 导致的重复执行
    这是最常见的原因,React 18在开发环境的Strict Mode下,会刻意重复执行useEffect、组件挂载/卸载流程,用来检测代码中潜在的内存泄漏、不一致性等问题。这种重复执行只在开发环境存在,生产环境会自动恢复正常。

如果想在开发环境验证这一点,可以暂时移除项目入口文件(比如index.js)里的<React.StrictMode>包裹,就能看到useEffect只执行一次了。

  1. 代码中cleanup函数的错误
    你的代码里存在缩进错误:return () => {socket.disconnect();}被写在了socket.on的回调函数内部,导致useEffect没有正确返回清理函数。这会导致组件卸载时socket没有断开,当Strict Mode触发组件重新挂载时,旧的socket可能还存在,加上新的socket,可能引发额外的请求或渲染问题。

修正后的代码应该把清理函数作为useEffect的直接返回值:

useEffect(() => {
    const socket = io("http://localhost:3001");
    let public_key;
    if (roomID.length == 0) {
      axios
        .post(
          `${domain}/chat/publickey?is_update_key=false`,
          {},
          { withCredentials: true }
        )
        .then((res) => {
          console.log(res);
          public_key = res.data.detail;
          console.log(public_key);
          socket.emit("admin_room", public_key, socket.id);
          axios
            .get(`${domain}/chat/admin/${public_key}?page=1&items_per_page=5`, {
              withCredentials: true,
            })
            .then((res) => {
              setRoomID((prev) => [...prev, res.data]);
            });
        })
        .catch((err) => {
          console.log(err);
        });
    }

    socket.on("customer_message", (data) => {
      // 你的socket处理逻辑
    });
    
    // 正确返回清理函数
    return () => {
      socket.disconnect();
    };
  }, []);
  1. 额外的状态更新触发渲染
    虽然useEffect只执行一次,但内部的setRoomID会触发组件渲染,如果roomID的初始值或者更新逻辑有问题,可能导致多次状态更新,但结合你说的请求执行两次,这个可能性较低,优先排查前两点。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 02:13:18