React中实现两个Highcharts AreaSpline图表共享图例
实现React中Highcharts AreaSpline图表的共享图例
问题概述
需要为两个AreaSpline类型的Highcharts图表实现共享图例功能:点击其中一个图表的图例项时,两个图表的对应系列同步显示/隐藏。但当前代码运行时报错:
Uncaught TypeError: Cannot read properties of undefined (reading 'options')
报错原因
sharedLegendOptions函数内使用this访问系列配置,但在React函数组件的独立函数中this指向undefined,导致读取this.options.id失败。- 配置
legendItemClick时直接执行了sharedLegendOptions(),而非传递函数引用,导致组件初始化时就触发错误逻辑。 - 通过
document.querySelector直接查询DOM获取图表实例的方式不可靠,不符合React组件生命周期规范。
修复方案
- 在父组件中创建
ref,分别绑定到两个图表组件上,用于安全获取Highcharts实例。 - 修改事件处理逻辑:通过点击事件参数获取当前系列名称,在另一个图表中找到同名系列并同步其显示状态。
- 将事件处理函数作为引用传递给
legendItemClick,避免初始化时执行。 - 仅在显示图例的图表上绑定点击事件。
修改后的完整代码
component.js
import React, { useRef } from "react"; import "bootstrap/dist/css/bootstrap.min.css"; import AreaSplineChart from "./areaspline"; import "./App.css"; const Component = ({ data, filteredData }) => { const chartData = filteredData.length !== 0 ? filteredData : data; // 创建ref用于获取两个图表实例 const chartOneRef = useRef(null); const chartTwoRef = useRef(null); const seriesOne = {"dts":["Feb 25, 2024","Feb 26, 2024","Feb 27, 2024","Feb 28, 2024","Feb 29, 2024","Mar 01, 2024","Mar 02, 2024","Mar 03, 2024","Mar 04, 2024","Mar 05, 2024","Mar 06, 2024","Mar 07, 2024"],"field":"One","legend":false,"series":[{"name":"page_views","data":[444774,484174,557434,492637,466673,409858,350902,445857,560202,554498,519825,507158],"color":"#DA70D6"},{"name":"article_views","data":[326770,340229,411025,346702,330603,297353,256416,328951,396708,392765,371077,365406],"color":"#FFD700"},{"name":"visits","data":[340812,362617,423843,371288,352045,318796,276761,347808,427239,423992,396206,391983],"color":"#6B8E23"},{"name":"return_visits","data":[156492,183181,209536,199166,186924,152693,130780,165849,217871,226510,207835,200054],"color":"#FF7F50"},{"name":"audio_play","data":[3292,3865,4913,4885,4547,4134,3762,2929,3681,4254,4444,4094],"color":"#20B2AA"},{"name":"video_play_e5","data":[687,644,457,476,776,428,572,462,1257,830,884,651],"color":"#FF8C00"}]} const seriesTwo = {"dts":["Feb 25, 2024","Feb 26, 2024","Feb 27, 2024","Feb 28, 2024","Feb 29, 2024","Mar 01, 2024","Mar 02, 2024","Mar 03, 2024","Mar 04, 2024","Mar 05, 2024","Mar 06, 2024","Mar 07, 2024"],"field":"Two","legend":true,"series":[{"name":"page_views","data":[946159,1140360,1213841,1177459,1185913,1078396,1077760,1088569,1386531,1389498,1303569,1217209],"color":"#DA70D6"},{"name":"article_views","data":[608024,733135,788907,764644,772883,713887,740001,719234,914251,906715,851080,783937],"color":"#FFD700"},{"name":"visits","data":[678800,818550,875175,856391,865832,793614,794805,794055,1001979,995969,936937,882231],"color":"#6B8E23"},{"name":"return_visits","data":[470732,558457,580674,569857,584406,520338,491274,534849,671634,666763,629567,596229],"color":"#FF7F50"},{"name":"audio_play","data":[16255,18510,22248,24258,25180,21633,18850,15823,19976,22023,22546,22746],"color":"#20B2AA"},{"name":"video_play_e5","data":[38688,74568,84344,96184,92342,75586,39355,35294,113613,124318,104916,650926],"color":"#FF8C00"}]} // 共享图例点击事件处理函数 const handleLegendItemClick = function (e) { const seriesName = this.name; // 获取另一个图表实例 const otherChart = chartOneRef.current?.chart; if (otherChart) { const targetSeries = otherChart.series.find(s => s.name === seriesName); if (targetSeries) { targetSeries.visible ? targetSeries.hide() : targetSeries.show(); } } // 阻止默认行为,避免当前图表自动切换状态(确保两个图表状态完全同步) return false; }; return ( <div className="trends clearfix w-100 column"> <h3 className="mt-1 ms-1" style={{ color: "#81b0d2" }}> <u>Trends</u> </h3> <div className="clearfix w-100 column"> <div className="clearfix w-100 column"> <div className="w-100 oneArea"> <AreaSplineChart ref={chartOneRef} dates={seriesOne.dts} nm={seriesOne.field} lgnd={seriesOne.legend} trendData={seriesOne.series} /> </div> <div className="w-100 twoArea"> <AreaSplineChart ref={chartTwoRef} dates={seriesTwo.dts} nm={seriesTwo.field} lgnd={seriesTwo.legend} trendData={seriesTwo.series} onLegendItemClick={handleLegendItemClick} /> </div> </div> </div> </div> ); }; export default Component;
areaspline.js
import React, { forwardRef, useMemo } from "react"; import Highcharts from "highcharts"; import HighchartsReact from "highcharts-react-official"; import highchartsAccessibility from "highcharts/modules/accessibility"; import highchartsExporting from "highcharts/modules/exporting"; import highchartsExportData from "highcharts/modules/export-data"; import dayjs from "dayjs"; import "dayjs/locale/es"; highchartsAccessibility(Highcharts); highchartsExporting(Highcharts); highchartsExportData(Highcharts); // 使用forwardRef传递ref到HighchartsReact组件 const AreaSplineChart = forwardRef(({ dates, nm, lgnd, trendData, onLegendItemClick }, ref) => { dayjs.locale("en"); const addCommas = (x) => x.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ","); const options = useMemo( () => ({ chart: { type: "areaspline", backgroundColor: "#283347", zoomType: "x", height: "25%", }, exporting: { enabled: true, }, navigation: { buttonOptions: { verticalAlign: "top", y: -10, x: -5, }, }, title: { text: nm, style: { color: "#fff", }, }, xAxis: { categories: dates, lineColor: "#fff", lineWidth: 3, labels: { style: { color: "#fff", }, }, }, yAxis: { gridLineColor: "#283347", lineColor: "#fff", lineWidth: 3, labels: { style: { color: "#fff", }, }, title: { text: "", }, opposite: false, }, legend: { enabled: lgnd, itemStyle: { color: "#fff", }, }, accessibility: { enabled: false, }, credits: { enabled: false, }, tooltip: { backgroundColor: "#283347", style: { color: "#fff" }, formatter: function () { const yFormat = addCommas(this.y); let tooltip = ` <span><b><u>${dayjs(this.key).format( "MMM DD, YYYY" )}</u></b>: ${yFormat}</span>`; return tooltip; }, useHTML: true, }, plotOptions: { series: { stacking: "normal", marker: { enabled: false, states: { hover: { enabled: true, }, }, }, events: { // 仅在显示图例的图表上绑定点击事件 legendItemClick: lgnd ? onLegendItemClick : undefined }, }, }, series: trendData, }), [nm, dates, lgnd, trendData, onLegendItemClick] ); return ( <HighchartsReact ref={ref} highcharts={Highcharts} containerProps={{ className: `${nm}-area` }} options={options} /> ); }); AreaSplineChart.displayName = "AreaSplineChart"; export default AreaSplineChart;
关键说明
- 通过
useRef和forwardRef获取图表实例,符合React组件通信规范,避免DOM查询的不可靠性。 handleLegendItemClick函数通过系列名称匹配另一个图表的对应系列,确保状态同步。- 返回
false阻止默认行为,避免当前图表自动切换状态,保证两个图表的显示状态完全一致。
内容的提问来源于stack exchange,提问作者nb_nb_nb
相关产品推荐
相关产品推荐

