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

React TS中使用Socket.IO时,如何避免组件全量重渲染仅渲染最新消息?

问题描述

我用Socket.IO实现实时消息交互,客户端基于React TS开发。每次将新消息添加到数组中,但客户端接收消息时,数组内所有消息都会重新渲染。如何避免组件全量重渲染,仅渲染最新的一条消息?

客户端组件代码

import * as React from 'react';
import { IUserProfileViewerProps } from './IUserProfileViewerProps';
import { FC, useEffect, useState } from 'react';
import { io } from "socket.io-client";

const socket = io("http://localhost:4000/")

const UserProfileViewer: FC<IUserProfileViewerProps> = ({ userDisplayName, userEmail }) => {
  const [messages, setMessages] = useState([]);

  useEffect(() => {
    socket.on("messageResponse", (data: any) => {
      setMessages([...messages, data])
    });
    console.log('messageResponse', messages);
  }, [socket, messages])

  return (
    <section>
      <ul>
        {messages.map((message) => {
          return (
            <li key={message.ts}>
              <div>{message.user}: {message.text}</div>
            </li>
          );
        })}
      </ul>
    </section>
  );
}
export default UserProfileViewer;

服务端JS文件代码

require("dotenv-extended").load();
const express = require("express");
const app = express();
const cors = require("cors");
const http = require("http").Server(app);
const PORT = 4000;
const socketIO = require("socket.io")(http, {
  cors: {
    origin: "http://localhost:3000",
  },
});
socketIO.on("connection", (socket) => {
  console.log(`⚡: ${socket.id} user just connected!`);

  socket.on("message", (data) => {
    socketIO.emit("messageResponse", data);
  });

  socket.on("disconnect", () => {
    console.log("🔥: A user disconnected");
    users = users.filter((user) => user.socketID !== socket.id);
    socketIO.emit("newUserResponse", users);
    socket.disconnect();
  });
});
http.listen(PORT, () => {
  console.log(`Server listening on ${PORT}`);
});
解决方案

1. 优化状态更新 + 修复Socket事件绑定问题

原代码中useEffect依赖messages,导致每次消息数组变化都会重新绑定messageResponse事件,既浪费资源又引发不必要重渲染。同时直接用[...messages, data]更新状态,依赖当前状态快照,可能出现状态不一致问题。

改成函数式更新并清理依赖:

useEffect(() => {
  const handleMessage = (data: any) => {
    // 基于最新状态更新,避免快照依赖问题
    setMessages(prevMessages => [...prevMessages, data]);
  };

  socket.on("messageResponse", handleMessage);
  
  // 组件卸载时清理事件,防止内存泄漏
  return () => {
    socket.off("messageResponse", handleMessage);
  };
}, [socket]); // 仅依赖socket,不再监听messages变化

2. 拆分独立消息组件并缓存

把单个消息项拆成独立组件,用React.memo包裹,只有当消息内容变化时该组件才会重渲染,旧消息组件会被缓存,避免无意义重复渲染。

创建MessageItem.tsx:

import { memo } from 'react';

interface MessageProps {
  message: {
    ts: string | number;
    user: string;
    text: string;
  };
}

const MessageItem = memo(({ message }: MessageProps) => {
  return (
    <li key={message.ts}>
      <div>{message.user}: {message.text}</div>
    </li>
  );
});

export default MessageItem;

主组件中使用:

// 导入组件
import MessageItem from './MessageItem';

// ...

return (
  <section>
    <ul>
      {messages.map((message) => (
        <MessageItem key={message.ts} message={message} />
      ))}
    </ul>
  </section>
);

改动说明

  • 函数式更新:确保每次基于最新消息数组添加新内容,同时简化useEffect依赖,避免频繁绑定事件。
  • React.memo:缓存单个消息组件,仅在消息内容变化时重渲染,彻底解决旧消息重复渲染问题。
  • 事件清理:防止组件卸载或重渲染时重复绑定Socket事件,避免内存泄漏和重复触发。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 08:35:23