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

React中点击Chart.js图例切换Annotation显示隐藏的问题求解

React-chartjs-2中点击图例切换注释显示的实现方案

问题背景

我正在使用chartjs-plugin-annotation插件在图表上显示一条垂直线,配置代码如下:

const options = {
    legend: {
      position: 'bottom',
      onClick: (e, legendItem, legend) => {
                const index = legendItem.datasetIndex;
                const ci = legend.chart;
                console.log(legendItem, legend);
                if (ci.isDatasetVisible(index)) {
                    ci.hide(index);
                    legendItem.hidden = true;
                    if(legendItem.text === "Current Time"){
                       //toggle annotation off
                    }
                } else {
                    ci.show(index);
                    legendItem.hidden = false;
                    if(legendItem.text === "Current Time"){
                        //toggle annotation on
                    }
                }
            }
    },
    annotation:{
        annotations: [{
            display: true,
            type: 'line',
            borderColor: rs.getPropertyValue('--CURRENT_TIMELINE'),
            borderWidth: 2,
            mode: 'vertical',
            xMin: moment.utc().subtract(30, "m").valueOf(),
            xMax: moment.utc().subtract(30, "m").valueOf()
        }]
    }
}

我已将options变量保存在组件状态(state)中,图表渲染代码如下:

<Bar options={this.state.options} data={this.state.data}/>

已知图例点击回调中的this指向Chart.js图例对象,而非组件实例,无法直接通过this.state修改注释的display属性,需要实现点击图例时切换注释的显示/隐藏状态。

使用版本:

  • chartjs-plugin-annotation@3.1.0
  • react-chartjs-2@5.2.0

解决方案

核心思路是让图例点击回调中的this指向组件实例,从而通过setState更新state中的配置,触发图表重新渲染。

方法一:定义组件类箭头函数作为回调

在组件中提前定义绑定好this的处理函数,再传入options配置:

// 组件类中定义处理函数(箭头函数自动绑定this)
handleLegendClick = (e, legendItem, legend) => {
  const index = legendItem.datasetIndex;
  const ci = legend.chart;

  if (ci.isDatasetVisible(index)) {
    ci.hide(index);
    legendItem.hidden = true;
    if (legendItem.text === "Current Time") {
      // 切换注释为隐藏
      this.setState(prevState => ({
        options: {
          ...prevState.options,
          annotation: {
            ...prevState.options.annotation,
            annotations: prevState.options.annotation.annotations.map(anno => ({
              ...anno,
              display: false
            }))
          }
        }
      }));
    }
  } else {
    ci.show(index);
    legendItem.hidden = false;
    if (legendItem.text === "Current Time") {
      // 切换注释为显示
      this.setState(prevState => ({
        options: {
          ...prevState.options,
          annotation: {
            ...prevState.options.annotation,
            annotations: prevState.options.annotation.annotations.map(anno => ({
              ...anno,
              display: true
            }))
          }
        }
      }));
    }
  }
}

// 在options中引用该函数
const options = {
  legend: {
    position: 'bottom',
    onClick: this.handleLegendClick
  },
  // 其余注释配置不变
  annotation:{
    annotations: [{
      display: true,
      type: 'line',
      borderColor: rs.getPropertyValue('--CURRENT_TIMELINE'),
      borderWidth: 2,
      mode: 'vertical',
      xMin: moment.utc().subtract(30, "m").valueOf(),
      xMax: moment.utc().subtract(30, "m").valueOf()
    }]
  }
}

方法二:直接使用箭头函数作为onClick回调

如果options是在组件内部定义的,可直接在onClick中使用箭头函数,继承外层作用域的this指向组件实例:

const options = {
    legend: {
      position: 'bottom',
      onClick: (e, legendItem, legend) => {
                const index = legendItem.datasetIndex;
                const ci = legend.chart;
                if (ci.isDatasetVisible(index)) {
                    ci.hide(index);
                    legendItem.hidden = true;
                    if(legendItem.text === "Current Time"){
                       // 切换注释为隐藏
                       this.setState(prevState => ({
                         options: {
                           ...prevState.options,
                           annotation: {
                             ...prevState.options.annotation,
                             annotations: prevState.options.annotation.annotations.map(anno => ({
                               ...anno,
                               display: false
                             }))
                           }
                         }
                       }));
                    }
                } else {
                    ci.show(index);
                    legendItem.hidden = false;
                    if(legendItem.text === "Current Time"){
                        // 切换注释为显示
                        this.setState(prevState => ({
                          options: {
                            ...prevState.options,
                            annotation: {
                              ...prevState.options.annotation,
                              annotations: prevState.options.annotation.annotations.map(anno => ({
                                ...anno,
                                display: true
                              }))
                            }
                          }
                        }));
                    }
                }
            }
    },
    annotation:{
        annotations: [{
            display: true,
            type: 'line',
            borderColor: rs.getPropertyValue('--CURRENT_TIMELINE'),
            borderWidth: 2,
            mode: 'vertical',
            xMin: moment.utc().subtract(30, "m").valueOf(),
            xMax: moment.utc().subtract(30, "m").valueOf()
        }]
    }
}

注意事项

  • 必须通过setState更新state,不能直接修改this.state.options,因为React依赖不可变的state更新来触发重新渲染。
  • 使用展开运算符(...)深拷贝配置对象的层级,避免修改原state中的引用类型数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 16:41:04