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

React组件渲染时内存占用持续升高,是否存在内存泄漏?

React WebSocket组件内存飙升问题排查

我开发了一个React组件,通过WebSocket连接服务器并监听接收消息进行展示。当消息列表超过10条时,会移除第一条消息并添加新消息,确保消息数组长度始终不超过10,且单条消息数据量极小。

组件代码如下:

import React, { useEffect, useState, useRef } from "react";
const ws = new WebSocket("ws://localhost:8000?uid=React");


export default function Discord () {
    const [messages, setMessages] = useState([]);

    ws.addEventListener("message", data => {
        if (messages.length >= 10) {
            setMessages(arr => arr.slice(1,arr.length).concat(JSON.parse(data.data)));
        } else setMessages([...messages, JSON.parse(data.data)]);  
    });
                                        
    return(
        <div class="bg-gray-900 rounded-lg h-full w-full">
            <div class="relative overflow-y-scroll scroll-smooth xl:h-4/5 h-5/6 top-0 w-full">
                <div class="absolute">
                    <MessageList messages={messages}/>
                </div>
            </div>
        </div>
    );
};
function MessageList ({ messages }) {
    const messagesEndRef = useRef(null)
    useEffect(() => {
        messagesEndRef.current.scrollIntoView()
    });
    return (
    <ul class="">
        {messages.map(message => <DiscordMessage messageData={message}/>)}
        <div ref={messagesEndRef}/>
    </ul>
    )
}

我已确认是该组件导致内存问题:移除组件后内存稳定在约600MB,启用后内存飙升至6GB,甚至导致Firefox崩溃。请问该内存问题是否源于组件渲染方式错误?


问题根源分析

你的内存问题主要不是渲染方式错误,而是WebSocket事件监听的注册逻辑有严重问题,同时搭配了不合理的useEffect使用,双重叠加导致内存泄漏:

  • WebSocket监听重复注册
    const ws = new WebSocket("...") 写在组件外部,每次组件重新渲染时,同一个WebSocket实例会被重复绑定N多个消息监听回调。每来一条新消息,所有绑定的回调都会触发并更新状态,导致状态更新次数指数级增长,内存里堆积大量闭包和状态快照,最终内存爆炸。

  • useEffect缺少依赖项
    MessageList里的useEffect(() => { ... })没有设置依赖数组,每次组件渲染(包括messages更新、父组件触发的重渲染)都会执行scrollIntoView,频繁的DOM操作加重内存负担,也造成不必要的性能消耗。

  • 组件卸载时未清理资源
    组件被卸载时,没有移除WebSocket的message监听,也没有关闭连接,导致WebSocket实例一直持有回调函数的引用,回调又持有组件的state和上下文,这些资源无法被垃圾回收,持续占用内存。

修复方案

按以下步骤修改代码,彻底解决内存问题:

1. 规范WebSocket实例的创建与监听管理

import React, { useEffect, useState, useRef } from "react";

export default function Discord () {
    const [messages, setMessages] = useState([]);
    const wsRef = useRef(null); // 用ref保存WebSocket实例,避免重复创建

    useEffect(() => {
        // 创建WebSocket实例
        const ws = new WebSocket("ws://localhost:8000?uid=React");
        wsRef.current = ws;

        // 定义消息处理回调
        const handleMessage = (data) => {
            const newMessage = JSON.parse(data.data);
            setMessages(prev => {
                return prev.length >= 10 
                    ? prev.slice(1).concat(newMessage) 
                    : [...prev, newMessage];
            });
        };

        // 绑定监听
        ws.addEventListener("message", handleMessage);

        // 组件卸载时清理资源
        return () => {
            ws.removeEventListener("message", handleMessage);
            ws.close(); // 关闭WebSocket连接
        };
    }, []); // 空依赖数组,仅在组件挂载时执行一次

    return(
        <div className="bg-gray-900 rounded-lg h-full w-full"> {/* React中用className替代class */}
            <div className="relative overflow-y-scroll scroll-smooth xl:h-4/5 h-5/6 top-0 w-full">
                <div className="absolute">
                    <MessageList messages={messages}/>
                </div>
            </div>
        </div>
    );
};

2. 修复MessageList的useEffect依赖项

function MessageList ({ messages }) {
    const messagesEndRef = useRef(null);

    useEffect(() => {
        messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
    }, [messages]); // 仅当messages变化时执行滚动操作

    return (
        <ul className="">
            {messages.map((message, index) => ( // 添加唯一key,优化React渲染逻辑
                <DiscordMessage 
                    key={index} // 若message有唯一id字段,优先使用id作为key
                    messageData={message}
                />
            ))}
            <div ref={messagesEndRef}/>
        </ul>
    )
}

额外注意点

  • React元素的类名必须用className而非class,这是React的语法规范。
  • 列表渲染时务必添加唯一key,帮助React正确识别元素变化,减少不必要的DOM操作,避免潜在内存问题。

内容的提问来源于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 00:25:02