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

React useEffect空依赖数组仍随状态更新重复执行问题排查

问题分析与解决方案

你的代码存在两个核心问题,导致你产生“空依赖useEffect重复执行”的误解:

1. WebSocket实例被重复创建

你在组件函数顶层声明let webSocket = new WebSocket(WS_URL);,而React组件每次state更新都会触发重渲染——也就是整个App函数会重新执行一遍。这意味着每次state变化时,都会新建一个WebSocket连接,但你绑定事件的useEffect只执行了一次,它操作的是第一次渲染时创建的实例,后续新建的实例并没有绑定onopen/onmessage事件,这会让你误以为是useEffect在重复执行,实际是组件重渲染带来的副作用。

2. 空依赖useEffect的执行逻辑

空依赖数组的useEffect确实只会在组件挂载时执行一次(等同于类组件的componentDidMount),不会在state更新时重新执行。你看到的异常现象,本质是组件重渲染导致的多余连接创建,而非useEffect本身重复执行。

修正后的代码

使用useRef持久化WebSocket实例,确保整个组件生命周期内只有一个连接,并将连接创建逻辑移到useEffect内部:

const App = () => {
  const [image, setImage] = useState({});
  const [bannerName, setBannerName] = useState('');
  const [bannerId, setBannerId] = useState(0);
  // 用useRef保存WebSocket实例,避免每次渲染重建
  const webSocketRef = useRef(null);

  useEffect(() => {
    // 在useEffect内创建连接,仅挂载时执行一次
    const webSocket = new WebSocket(WS_URL);
    webSocketRef.current = webSocket;

    webSocket.onopen = (evt) => {
      console.log('On open web socket: ', evt);
    };
    
    webSocket.onmessage = (evt) => {
      console.log('SERVER DATA: ', evt.data);
    };

    console.log('Ready state: ', webSocket.readyState);

    // 组件卸载时关闭连接,防止内存泄漏
    return () => {
      webSocket.close();
    };
  }, []);

  const updateImage = (evt) => {
    setImage(evt.target.files[0]);
  };

  const updateBannerName = (evt) => {
    setBannerName(evt.target.value);
  };

  const updateBannerID = (evt) => {
    setBannerId(Number(evt.target.value));
  };

  // ... 省略return部分
};

关键修正点

  • useRef创建的对象在组件生命周期内保持引用不变,确保始终操作同一个WebSocket实例。
  • 将连接创建和事件绑定逻辑移入useEffect,彻底避免重复创建连接。
  • 添加清理函数,在组件卸载时关闭WebSocket连接,避免资源浪费和内存泄漏。

内容的提问来源于stack exchange,提问作者Bruno Gonzalez Torres

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 00:47:39