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

React中WebSocket接收消息时状态数组不扩容?问题排查与解决

问题原因与解决方案

问题根源

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

  • useEffect的依赖数组为空,意味着它只会在组件首次渲染时执行一次。此时socket.onmessage回调函数捕获的messages是初始的空数组状态。
  • 后续组件重新渲染时,这个回调函数并不会重新创建,依然保留着首次渲染时捕获的空数组引用。所以每次接收消息时,执行的都是[...[], 新消息],导致数组永远只有1个元素,还会覆盖之前的内容。
  • 而addMessage函数每次组件渲染都会重新创建,能捕获到最新的messages状态,所以发送消息时可以正常追加。

解决方法

使用setMessages的函数式更新语法,直接基于最新的状态快照更新数组,彻底避免闭包带来的旧值问题:

修改后的关键代码

socket.onmessage = msg => {
    const parsedMessage = JSON.parse(msg.data)
    // 使用函数式更新,基于最新的prevMessages追加新消息
    setMessages(prevMessages => [...prevMessages, {className: "other-message", body: parsedMessage.body}])
};

完整优化后的代码

import './App.css';
import { useState, useEffect } from "react"

const socket = new WebSocket("ws://localhost:8080/ws");

function App() {
    const [messages, setMessages] = useState([])
    const [inputValue, setInputValue] = useState("")

    useEffect(() => {
      socket.onopen = () => {
        console.log("connected")
      }

      socket.onmessage = msg => {
          const parsedMessage = JSON.parse(msg.data)
          // 函数式更新,确保基于最新状态
          setMessages(prevMessages => [...prevMessages, {className: "other-message", body: parsedMessage.body}])
      };

      socket.onclose = event => {
          console.log("Socket Closed Connection: ", event);
      };

      socket.onerror = error => {
          console.log("Socket Error: ", error);
      };

    }, [])

    const addMessage = () => {
      socket.send(inputValue);
      // 同样建议把发送消息的更新也改成函数式,避免快速点击等场景的状态丢失
      setMessages(prevMessages => [...prevMessages, {className: "message", body: inputValue}])
      setInputValue("")
    }

    console.log("messages", messages);

    return (
      <div className="App">
          <h1>Chat</h1>
          <div className='chat-window'>
            {
              messages.map((message, i) => {
                return <div key={i} className={message.className}>{message.body}</div>
              })
            }
          </div>
          <div className='input-wrapper'>
            <input value={inputValue} onChange={e => setInputValue(e.target.value)}/>
            <button onClick={addMessage}>Send</button>
          </div>
      </div>
    );
}

export default App;

为什么这样有效?

函数式更新会让React自动传递当前最新的状态快照(这里是prevMessages)给你,不管回调函数是何时创建的,都能拿到最准确的当前状态,彻底避开闭包陷阱导致的旧值引用问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 12:55:17