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

react-chartjs-2环形图标题不更新问题求助

环形图标题渲染旧值问题排查与解决

问题描述

使用react-chartjs-2实现环形图时,父组件传递的title props更新后,组件外部打印title能得到正确的新值,但插件的beforeDraw函数内部打印的却是旧的title,导致环形图中心渲染的标题始终是旧值。相关代码如下:

const Doughnut = (props) => {
  const { title, labels, colors, projects, percentages, width, height } = props;
  console.log(`title:${title}`); //this logs proper title
  const plugins = [
    {
      beforeDraw: function (chart) {
        console.log(`title:${title}`); //this logs previous title and thus renders previous title
        var width = chart.width,
          height = chart.height,
          ctx = chart.ctx;
        ctx.restore();
        var fontSize = (height / 240).toFixed(2);
        ctx.font = fontSize + "em sans-serif";
        ctx.textBaseline = "top";
        var text = `${title}`,
          textX = Math.round((width - ctx.measureText(text).width) / 2),
          textY = height / 2;
        ctx.fillText(title, textX, textY);
        ctx.save();
      },
    },
  ];

  const data = {
    datasets: [
      {
        data: percentages,
        backgroundColor: colors,
      },
    ],
  };

  return (
    <DoughnutChart
      data={data}
      plugins={plugins}
      width={width}
      height={height}
    />
  );
};

问题原因

Chart.js的插件实例存在缓存机制,组件首次渲染时注册的插件会被复用。原代码中,beforeDraw函数创建时捕获了当时的title闭包值;后续title props更新时,虽然组件重新渲染生成了新的plugins数组,但Chart.js并未重新注册新的插件实例,执行的仍是第一次注册的beforeDraw函数,自然只能拿到旧的title值。

解决方案

通过useCallback和useMemo确保title更新时,beforeDraw函数和plugins数组生成新的引用,触发Chart.js重新注册插件,从而捕获最新的title值。

修改后的代码

import { useCallback, useMemo } from 'react';
import { Doughnut as DoughnutChart } from 'react-chartjs-2';

const Doughnut = (props) => {
  const { title, labels, colors, projects, percentages, width, height } = props;
  console.log(`title:${title}`);

  // 使用useCallback包裹beforeDraw,依赖title确保更新时生成新函数
  const beforeDraw = useCallback((chart) => {
    console.log(`title:${title}`);
    const ctx = chart.ctx;
    const chartWidth = chart.width;
    const chartHeight = chart.height;
    
    ctx.restore();
    const fontSize = (chartHeight / 240).toFixed(2);
    ctx.font = `${fontSize}em sans-serif`;
    ctx.textBaseline = "top";
    
    const text = `${title}`;
    const textX = Math.round((chartWidth - ctx.measureText(text).width) / 2);
    const textY = chartHeight / 2;
    
    ctx.fillText(title, textX, textY);
    ctx.save();
  }, [title]);

  // 使用useMemo生成plugins数组,依赖beforeDraw确保引用更新
  const plugins = useMemo(() => [{ beforeDraw }], [beforeDraw]);

  const data = {
    datasets: [
      {
        data: percentages,
        backgroundColor: colors,
      },
    ],
  };

  return (
    <DoughnutChart
      data={data}
      plugins={plugins}
      width={width}
      height={height}
    />
  );
};

关键修改点

  • 用useCallback包裹beforeDraw函数,将title作为依赖项,当title变化时,函数会重新创建,捕获最新的title值。
  • 用useMemo生成plugins数组,依赖beforeDraw函数,确保beforeDraw更新时,plugins数组的引用也更新,触发Chart.js重新识别插件并执行新的beforeDraw逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 01:21:05