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

React自定义Bar组件触发无限渲染导致浏览器崩溃问题排查

无限渲染问题修复方案

问题根源

  • 核心原因:自定义Bar组件中完全没必要用useState存储Chart实例,你代码中并未消费这个状态值,每次调用setChartInstance都会触发组件重渲染,形成循环触发链:执行useEffect创建实例→调用setState触发重渲染→useEffect依赖变更再次执行→回到第一步
  • 次要问题:useEffect依赖数组错误加入了整个chartContainer ref对象,同时未添加清理函数销毁旧的Chart实例,也未判断自定义图表类型是否已经注册,重复修改Chart全局配置也会加重异常。

你之前猜测的const声明组件和问题无关,React组件不管是函数声明还是const声明的箭头函数组件,都不会引发无限渲染,完全是自定义Bar组件内部的状态使用和依赖配置错误导致的问题。

修复后的Bar组件代码

/* eslint-disable prefer-rest-params */
import React, { useEffect, useRef } from 'react';
import { Chart } from 'chart.js';

import { barChartOptions } from './config';

// 自定义图表类型只需要注册一次,放到组件外避免重复注册
let shadowControllerRegistered = false;

const Bar = ({ data, shadow = false }) => {
  const chartContainer = useRef(null);
  // 用ref存实例,不需要用state,不会触发重渲染
  const chartInstanceRef = useRef(null);

  useEffect(() => {
    // 先销毁旧的实例,避免重复创建
    if (chartInstanceRef.current) {
      chartInstanceRef.current.destroy();
    }

    if (chartContainer.current) {
      // 只注册一次自定义阴影柱状图类型
      if (shadow && !shadowControllerRegistered) {
        Chart.defaults.global.datasets.barWithShadow =
          Chart.defaults.global.datasets.bar;
        Chart.defaults.barWithShadow = Chart.defaults.bar;
        Chart.controllers.barWithShadow = Chart.controllers.bar.extend({
          draw(ease) {
            Chart.controllers.bar.prototype.draw.call(this, ease);
            const {
              chart: { ctx },
            } = this;
            ctx.save();
            ctx.shadowColor = 'rgba(0,0,0,0.2)';
            ctx.shadowBlur = 7;
            ctx.shadowOffsetX = 5;
            ctx.shadowOffsetY = 7;
            ctx.responsive = true;
            Chart.controllers.bar.prototype.draw.apply(this, arguments);
            ctx.restore();
          },
        });
        shadowControllerRegistered = true;
      }

      const context = chartContainer.current.getContext('2d');
      // 实例存到ref里,不要用state存,避免触发重渲染
      chartInstanceRef.current = new Chart(context, {
        type: shadow ? 'barWithShadow' : 'bar',
        options: barChartOptions,
        data,
      });
    }

    // 清理函数:组件卸载或者依赖变更时销毁实例
    return () => {
      if (chartInstanceRef.current) {
        chartInstanceRef.current.destroy();
      }
    };
  // 依赖只留需要监听变化的data和shadow,不需要加ref对象
  }, [data, shadow]);

  return <canvas ref={chartContainer} />;
};

export default Bar;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 13:39:02