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

React使用自定义Bar柱状图组件触发无限循环问题求助

问题根因
  • 自定义Bar组件的useEffect未添加实例销毁逻辑,每次触发都会创建新的Chart实例,旧实例残留会引发重复更新
  • 错误将useRef返回的chartContainer加入effect依赖,ref引用本身全程稳定无需加入依赖,反而可能触发不必要的effect执行
  • shadow为true时每次effect执行都会重复注册全局的barWithShadow图表类型,修改Chart.js全局配置引发连锁重绘
  • Chart.js默认会直接修改传入的data对象添加内部属性,可能引发意外的状态变更判定
修复方案

第一步:修改自定义Bar组件

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

// 把自定义图表类型注册逻辑移到组件外,全局只执行一次
if (typeof Chart !== 'undefined') {
  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;
      Chart.controllers.bar.prototype.draw.apply(this, arguments);
      ctx.restore();
    },
  });
}

const Bar = ({ data, shadow = false }) => {
  const chartContainer = useRef(null);
  const [, setChartInstance] = useState(null);
  const chartInstanceRef = useRef(null);

  useEffect(() => {
    // 先销毁旧实例
    if (chartInstanceRef.current) {
      chartInstanceRef.current.destroy();
      chartInstanceRef.current = null;
    }

    if (chartContainer?.current) {
      const context = chartContainer.current.getContext('2d');
      // 传入data的深拷贝,避免Chart修改原对象
      const clonedData = JSON.parse(JSON.stringify(data));
      const newChartInstance = new Chart(context, {
        type: shadow ? 'barWithShadow' : 'bar',
        options: barChartOptions,
        data: clonedData,
      });
      chartInstanceRef.current = newChartInstance;
      setChartInstance(newChartInstance);
    }

    // 清理函数
    return () => {
      if (chartInstanceRef.current) {
        chartInstanceRef.current.destroy();
        chartInstanceRef.current = null;
      }
    };
  // 移除chartContainer依赖
  }, [data, shadow]);

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

export default Bar;

第二步:修改父组件MyComponent

const MyComponent = ({ someInput }) => {
  // 初始值和后续赋值类型统一,改为对象
  const [state2, setState2] = useState({
    labels: [],
    datasets: []
  });

  useEffect(() => {
    setState2({
      labels: ["January", "February", "March", "April", "May"],
      datasets: [
        {
          label: "Rainfall",
          backgroundColor: [
            "#B21F00",
            "#C9DE00",
            "#2FDE00",
            "#00A6B4",
            "#6800B4",
          ],
          hoverBackgroundColor: [
            "#501800",
            "#4B5000",
            "#175000",
            "#003350",
            "#35014F",
          ],
          data: [65, 59, 80, 81, 56],
        },
      ],
    });
  }, []);

  return (
    <div className="search">
      <Bar data={state2} />
    </div>
  );
};

export default MyComponent;

内容的提问来源于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 07:45:06