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

React队列组件使用shift()更新数组后平滑滚动失效问题

问题分析与解决

你的问题出在错误修改原状态数组,且连续调用setMessages导致状态更新混乱,具体问题点:

  1. messages.shift()会直接修改原状态数组,违反React状态不可变原则;同时shift()返回的是被移除的单个元素,你把它传给setMessages,会让messages从数组变成单个对象,后续[...messages, JSON.parse(data.data)]会因messages不是数组而出错,组件渲染异常后滚动功能自然失效。
  2. 连续两次调用setMessages,React会批量处理状态更新,最终状态不符合预期,进一步导致滚动逻辑的依赖(messages)异常。

修正后的代码

ws.addEventListener("message", data => {
  const newMsg = JSON.parse(data.data);
  // 用函数式更新获取最新状态,避免闭包捕获旧值
  setMessages(prevMessages => {
    // 消息数达5条时,截取后4条再添加新消息
    if (prevMessages.length >= 5) {
      return [...prevMessages.slice(1), newMsg];
    }
    // 否则直接追加新消息
    return [...prevMessages, newMsg];
  });
});

const messagesEndRef = useRef(null);
useEffect(() => {
  messagesEndRef.current?.scrollIntoView({ 
    behavior: 'smooth', 
    block: 'nearest', 
    inline: 'start' 
  });
}, [messages]);

const MessageList = ({ messages }) => (
  <ul className=""> {/* React中需用className替代class */}
    {messages.map((message, index) => (
      <DiscordMessage key={index} messageData={message} /> {/* 循环渲染必须加key */}
    ))}
    <div ref={messagesEndRef} />    
  </ul>
);

关键改动说明

  • 函数式更新状态:setMessages(prevMessages => {...})确保拿到最新的状态值,避免WebSocket回调的闭包问题捕获旧的messages。
  • 保持状态不可变:用slice(1)创建原数组的子数组(从第2个元素到末尾),再拼接新消息,全程不修改原状态数组。
  • 修复React规范问题:替换class为className,循环渲染添加key属性,避免React警告。
  • 空值保护:messagesEndRef.current?.scrollIntoView避免初始渲染时current为null的报错。

修改后,messages始终保持数组状态,组件渲染正常,滚动逻辑能稳定触发。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 15:02:15