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
相关产品推荐
相关产品推荐

