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

React中接收WebSocket消息后调用send报socket未定义问题

问题原因

核心是React state更新的异步特性 + 闭包陷阱共同导致的问题:

  • useEffect 内部的执行顺序为:创建WebSocket实例 → 绑定message事件回调 → 调用setSocket触发state更新。React的state更新是异步操作,不会立即生效。
  • 你使用的回声服务会在连接建立成功后立即推送第一条消息,这条消息的触发时机可能早于state更新完成的时机。此时message回调中调用parseMessage,sendMessage读取的是当前组件渲染闭包中的socket state,还是初始的undefined,就会抛出你看到的报错。
  • 后续点击按钮正常是因为此时state早已更新完成,socket已经有有效值,所以不会报错。
修复方案

方案1:使用useRef存储WebSocket实例(推荐)

useRef的更新是同步的,且不受闭包影响,非常适合存储这类不需要触发组件重渲染的全局实例:

import React, { useState, useEffect, useRef } from "react";

export default function Component(props) {
  const socketRef = useRef();

  const parseMessage = (msg) => {
    if (msg[0] !== "R") sendMessage("123");
  };

  const sendMessage = (msg) => {
    socketRef.current?.send(msg);
  };

  useEffect(() => {
    const socket = new WebSocket("wss://ws.ifelse.io/");
    // 先给ref赋值再绑定事件,保证回调能拿到最新实例
    socketRef.current = socket;

    socket.addEventListener("message", ({ data }) => {
      parseMessage(data);
    });

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

  const sendMsg = () => {
    socketRef.current?.send("test");
  };

  return <button onClick={() => sendMsg("clicked")}>send msg</button>;
}

方案2:直接使用闭包内的局部socket实例

如果不想修改存储方式,也可以在message回调中直接调用局部作用域的socket,避免依赖异步更新的state:

useEffect(() => {
  const socket = new WebSocket("wss://ws.ifelse.io/");
  socket.addEventListener("message", ({ data }) => {
    // 直接用当前闭包里的socket变量,不走state
    if (data[0] !== "R") {
      socket.send("123");
    }
  });
  setSocket(socket);
  return () => socket.close();
}, []);

方案3:添加存在性判断

在调用send方法前先判断socket是否存在,兼容state更新的窗口期:

const sendMessage = (msg) => {
  if (socket) socket.send(msg);
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 05:48:03