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

React聊天应用:会话置顶时避免聊天组件重渲染

解决方案

1. 拆分Context状态,隔离组件依赖

把原来的单一Chat Context拆分为两个独立的Context,让不同组件只订阅自身需要的状态:

  • SessionContext:仅维护会话列表数据及更新方法,供红色边框的会话列表组件使用。
  • MessageContext:维护当前选中会话的消息列表、当前会话ID及相关操作,供蓝色边框的聊天消息组件使用。

示例代码:

// SessionContext.js
import { createContext, useState, useContext } from 'react';

const SessionContext = createContext();

export const SessionProvider = ({ children }) => {
  const [sessions, setSessions] = useState([]);

  // 更新会话并自动排序置顶
  const updateSessionWithNewMessage = (message) => {
    setSessions(prev => {
      // 更新对应会话的最后消息时间
      const updatedSessions = prev.map(session => 
        session.id === message.sessionId 
          ? { ...session, lastMessageTime: message.timestamp } 
          : session
      );
      // 按时间降序排序,新消息的会话自动置顶
      return [...updatedSessions].sort((a, b) => b.lastMessageTime - a.lastMessageTime);
    });
  };

  return (
    <SessionContext.Provider value={{ sessions, updateSessionWithNewMessage }}>
      {children}
    </SessionContext.Provider>
  );
};

export const useSession = () => useContext(SessionContext);
// MessageContext.js
import { createContext, useState, useContext, useMemo } from 'react';

const MessageContext = createContext();

export const MessageProvider = ({ children }) => {
  const [allMessages, setAllMessages] = useState([]);
  const [currentSessionId, setCurrentSessionId] = useState(null);

  // 缓存当前会话的消息,避免不必要的引用变化
  const currentMessages = useMemo(() => {
    return allMessages.filter(msg => msg.sessionId === currentSessionId);
  }, [allMessages, currentSessionId]);

  // 添加新消息到全局列表
  const addNewMessage = (message) => {
    setAllMessages(prev => [...prev, message]);
  };

  return (
    <MessageContext.Provider value={{ currentMessages, currentSessionId, setCurrentSessionId, addNewMessage }}>
      {children}
    </MessageContext.Provider>
  );
};

export const useMessage = () => useContext(MessageContext);

2. 优化会话列表渲染逻辑

不要在JSX中直接调用list.sortByTime()——这种写法会在每次渲染时生成新数组,触发不必要的重渲染。改为在状态更新阶段完成排序,渲染时直接使用已排序好的sessions数组:

// 会话列表组件
import { useSession } from './SessionContext';

const SessionList = () => {
  const { sessions } = useSession();

  return (
    <div className="session-list">
      {sessions.map(session => (
        <SessionItem key={session.id} session={session} />
      ))}
    </div>
  );
};

3. 用React.memo锁定聊天消息组件重渲染时机

给蓝色边框的聊天消息组件套上React.memo,确保只有当currentMessages或currentSessionId变化时才触发重渲染:

// 聊天消息组件
import { useMessage } from './MessageContext';
import React from 'react';

const ChatMessages = React.memo(() => {
  const { currentMessages } = useMessage();

  return (
    <div className="chat-messages">
      {currentMessages.map(msg => (
        <MessageItem key={msg.id} message={msg} />
      ))}
    </div>
  );
});

4. 调整WebSocket消息处理逻辑

在Stomp Client的消息回调中,分别调用两个Context的更新方法,精准控制组件重渲染范围:

// WebSocket连接组件
import { useSession, useMessage } from './contexts';

const WebSocketHandler = () => {
  const { updateSessionWithNewMessage } = useSession();
  const { addNewMessage, currentSessionId } = useMessage();

  useEffect(() => {
    const stompClient = createStompClient(); // 你的Stomp Client初始化逻辑
    stompClient.subscribe('/topic/messages', (message) => {
      const parsedMsg = JSON.parse(message.body);
      // 更新会话列表,自动排序置顶
      updateSessionWithNewMessage(parsedMsg);
      // 仅当新消息属于当前选中会话时,才更新消息列表
      if (parsedMsg.sessionId === currentSessionId) {
        addNewMessage(parsedMsg);
      }
    });

    return () => stompClient.disconnect();
  }, [updateSessionWithNewMessage, addNewMessage, currentSessionId]);

  return null;
};

这样处理后,非当前会话收到新消息时,只会触发会话列表组件重渲染(对应会话置顶),聊天消息组件完全不受影响;只有当前会话收到新消息时,聊天消息组件才会更新,同时保留输入框焦点、滚动位置等状态。

内容的提问来源于stack exchange,提问作者Vu Hong Ha

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 10:52:20