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
相关产品推荐
相关产品推荐

