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

在React中如何正确使用socket.on?聊天应用接收消息状态丢失解决方法

问题根源

你遇到的是React的闭包陷阱问题:

  • 你把socket.on的注册逻辑放在了依赖项为空数组的useEffect中,该钩子只会在组件首次挂载时执行1次,回调函数捕获的messages永远是初始值[]
  • 每次收到新消息调用setMessages时,都是基于初始空数组扩展,自然会覆盖之前已经更新的消息列表

修复方案

1. 状态更新改为函数式写法

setMessages支持传入回调函数,参数为最新的状态值,不需要依赖外部捕获的messages变量,从根源避免闭包问题。

2. 添加socket事件清理逻辑

组件卸载时要移除socket.on注册的事件回调,否则会出现重复触发、内存泄漏问题。

3. 可选优化:sendMessage方法也改为函数式更新,避免其他场景下的闭包异常

修复后完整代码如下:

export const Room = ({ socket }) => {
  const [messages, setMessages] = useState([]);
  const [input, setInput] = useState("");
  const { id } = useParams();
  
  useEffect(() => {
    socket.emit("join room", id);
  }, [id, socket]); // 补充缺失的依赖项,符合React hooks语法规范

  useEffect(() => {
    const handleMessage = (message) => {
      // 改为函数式更新,直接读取最新的消息列表
      setMessages(prevMessages => [
        ...prevMessages,
        { message: message, createdBy: "other", id: uuidv4() },
      ]);
    };
    socket.on("message", handleMessage);
    // 组件卸载时清理事件监听
    return () => {
      socket.off("message", handleMessage);
    };
  }, [socket]); // 补充依赖项

  const sendMessage = () => {
    console.log("send");
    socket.emit("message", input);
    // 同样改为函数式更新,规避闭包问题
    setMessages(prevMessages => [
      ...prevMessages,
      { message: input, createdBy: "me", id: uuidv4() },
    ]);
    // 可选:发送后清空输入框提升使用体验
    setInput("");
  };

  return (
    <div className="flex justify-center text-center">
      <div className="w-screen h-screen px-2 max-w-4xl mt-10">
        <p className="text-3xl">Code: {id}</p>
        <div className="bg-white w-full h-3/4 rounded-xl border-2 border-black overflow-y-auto">
          {messages.map((message) => (
            <Message
              text={message.message}
              createdBy={message.createdBy}
              key={message.id}
            />
          ))}
        </div>
        <div className="flex flex-row border-2 border-black mt-2 rounded-xl p-2 bg-white">
          <input
            className="flex-grow border-none focus:outline-none"
            value={input} // 补充value绑定,将输入框改为受控组件避免异常
            onChange={(e) => setInput(e.target.value)} // 替换为更通用的onChange事件
          />
          <button
            className=" bg-green-500 rounded-xl px-2 py-1"
            onClick={sendMessage}
          >
            Send
          </button>
        </div>
      </div>
    </div>
  );
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 07:06:03