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

React Chart.js插件渲染陈旧数据:中心文本显示错误排查

Doughnut图表搜索后中心文本显示错误问题排查

我通过插件渲染一系列带中心文本的Doughnut图表,初始渲染效果符合预期。但实现客户端搜索功能并重新渲染图表系列后,插件获取了无关的数据集,导致中心文本显示错误。
注意:搜索后Doughnut图表本身渲染正常,仅中心文本存在问题。
已尝试方案:将插件方法从beforeDatasetsDraw更换为其他插件生命周期方法,尝试解决渲染生命周期相关问题,但未取得效果。

截图说明

  • 初始渲染时,Doughnut图表和中心文本显示正常
  • 搜索后,中心文本显示错误

Doughnut组件代码

<>
  {props.microservices &&
    Array.isArray(props.microservices) &&
    props.microservices.map((e) => (
      <>
        <p>{e?.serviceInfo?.name}</p>
        <DoughnutChart
          containerClassName="h-[75px] flex justify-center"
          data={{
            datasets: [
              {
                data: [Math.round(e.cpu), 100 - Math.round(e.cpu)],
                weight: 3,
                backgroundColor:
                  Math.round(e.cpu) === 0
                    ? ["transparent", "transparent"]
                    : ["#BB723F", "#21262D"],
                borderColor: "#232323",
                borderWidth: 0,
              },
              {
                weight: 3,
                data: [100],
                borderColor: "#232323",
                borderWidth: 0,
                backgroundColor:
                  Math.round(e.cpu) === 0 || Math.round(e.memory) === 0
                    ? ["transparent"]
                    : ["#29292A"],
              },
              {
                data: [Math.round(e.memory), 100 - Math.round(e.memory)],
                weight: 3,
                borderColor: "#232323",
                borderWidth: 0,
                backgroundColor:
                  Math.round(e.memory) === 0
                    ? ["transparent", "transparent"]
                    : ["#31737B", "transparent"],
              },
            ],
          }}
          options={{
            responsive: true,
            cutout: "80%",
            plugins: {
              tooltip: {
                enabled: false,
              },
              datalabels: {
                display: false,
              },
            },
          }}
          plugins={[
            centerText({
              text: e.serviceInfo?.name,
              fillColor:
                Math.round(e.cpu) === 0 || Math.round(e.memory) === 0
                  ? "#3E4249"
                  : "#0C1117",
              arcX: props.isPopUp ? 30 : 34,
              arcY: 38,
              arcRadius: props.isPopUp ? 24 : 27,
              arcStart: 0,
              arcEnd: 2 * Math.PI,
              activePods: e.serviceInfo?.activePods,
              totalPods: 16,
              isDisabledDoughNut:
                Math.round(e.cpu) === 0 || Math.round(e.memory) === 0,
            }),
          ]}
          isDisabledDoughNut={
            Math.round(e.cpu) === 0 || Math.round(e.memory) === 0
          }
          onClickHandler={(_) => {
            const {
              cpu,
              memory,
              traffic,
              errorRate,
              serviceInfo: { instance, activePods },
            } = e;
            props.setOverLayData({
              cpu,
              memory,
              traffic,
              errorRate,
              activePods,
              totalPods: 99,
              title: instance?.split("-").join(" "),
            });
            props.op.current?.toggle(_);
          }}
        />
      </>
    ))}
</>

Center Text Plugin代码

const centerText = ({
  text = "HELLO WORLD",
  fillColor = "#F86E6E",
  textColor = "#fff",
  arcX = 220,
  arcY = 230,
  arcRadius = 200,
  arcStart = 0,
  arcEnd = 2 * Math.PI,
  isDisabledDoughNut = false,
  activePods = 0,
  totalPods = 0,
}) => ({
  id: "centerText",
  beforeDatasetsDraw(chart) {
    let warningIcon;
    if (isDisabledDoughNut) {
      warningIcon = new Image();
      warningIcon.src = `data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxMS41ODIiIGhlaWdodD0iMTAiIHZpZXdCb3g9IjAgMCAxMS41ODIgMTAiPgogIDxwYXRoIGlkPSJ3YXJuaW5nX0ZJTEwxX3dnaHQzMDBfR1JBRDBfb3BzejI0IiBkPSJNNzQuNjE4LTgzMGw1Ljc5MS0xMEw4Ni4yLTgzMFptNS43OTEtMS41MzlhLjQ0Ni40NDYsMCwwLDAsLjMyOS0uMTMzLjQ0Ni40NDYsMCwwLDAsLjEzMy0uMzI5LjQ0Ny40NDcsMCwwLDAtLjEzMy0uMzI5LjQ0Ny40NDcsMCwwLDAtLjMyOS0uMTMzLjQ0Ny40NDcsMCwwLDAtLjMyOS4xMzMuNDQ3LjQ0NywwLDAsMC0uMTMzLjMyOS40NDYuNDQ2LDAsMCwwLC4xMzMuMzI5QS40NDYuNDQ2LDAsMCwwLDgwLjQwOS04MzEuNTM2Wm0tLjQyOS0xLjQ5NGguODU3di0yLjg1N2gtLjg1N1oiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC03NC42MTggODM5Ljk5NykiIGZpbGw9IiNmYmZiZmMiLz4KPC9zdmc+Cg==`;
    }
    const { ctx } = chart;
    ctx.save();
    const x = chart.getDatasetMeta(0).data[0].x;
    const y = chart.getDatasetMeta(0).data[0].y;

    ctx.beginPath();
    ctx.arc(arcX, arcY, arcRadius, arcStart, arcEnd);
    ctx.fillStyle = fillColor;
    ctx.fill();
    ctx.stroke();
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    ctx.font = "12px Helvetica";
    ctx.fillStyle = textColor;
    ctx.fillText(text, x - 1, y - 5);

    if (!isDisabledDoughNut) {
      ctx.font = "8px Helvetica";
      ctx.fillText(`${activePods}/${totalPods}`, x, y + 10);
    }

    if (isDisabledDoughNut && warningIcon.complete) {
      ctx.drawImage(warningIcon, x - 5, y + 5, 10, 10);
    }
  },
});

问题原因与解决方案

核心原因

  1. 所有图表的插件共用同一个id: "centerText",Chart.js会复用旧插件实例,导致搜索后新图表的插件参数被旧实例缓存覆盖。
  2. 通过chart.getDatasetMeta(0).data[0].x获取中心坐标的方式,在图表复用/重建时可能指向旧数据集的位置。

修复步骤

  1. 给每个插件实例分配唯一ID
    修改插件调用和定义,确保每个图表的插件独立:

    // 组件中调用插件时传入唯一ID
    plugins={[
      centerText({
        id: `centerText-${e.serviceInfo?.name}`, // 用服务名称做唯一标识
        text: e.serviceInfo?.name,
        // 其他参数保持不变
      }),
    ]}
    
    // 插件定义中接收并使用传入的ID
    const centerText = ({
      id = "centerText",
      text = "HELLO WORLD",
      // 其他参数
    }) => ({
      id,
      beforeDatasetsDraw(chart) {
        // 原有逻辑不变
      },
    });
    
  2. 正确获取当前图表的中心坐标
    放弃依赖数据集索引,改用图表自身区域计算中心:

    // 替换插件中x、y的获取逻辑
    const chartArea = chart.chartArea;
    const x = (chartArea.left + chartArea.right) / 2;
    const y = (chartArea.top + chartArea.bottom) / 2;
    
  3. 给图表组件添加唯一key
    确保React在搜索后能正确重建图表组件,而非复用旧实例:

    props.microservices.map((e, index) => (
      <div key={e.serviceInfo?.name || index}>
        <p>{e?.serviceInfo?.name}</p>
        <DoughnutChart
          // 原有属性不变
        />
      </div>
    ))
    

以上三步结合即可解决搜索后中心文本显示错误的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 16:04:52