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

React中实现两个Highcharts AreaSpline图表共享图例

实现React中Highcharts AreaSpline图表的共享图例

问题概述

需要为两个AreaSpline类型的Highcharts图表实现共享图例功能:点击其中一个图表的图例项时,两个图表的对应系列同步显示/隐藏。但当前代码运行时报错:

Uncaught TypeError: Cannot read properties of undefined (reading 'options')

报错原因

  1. sharedLegendOptions函数内使用this访问系列配置,但在React函数组件的独立函数中this指向undefined,导致读取this.options.id失败。
  2. 配置legendItemClick时直接执行了sharedLegendOptions(),而非传递函数引用,导致组件初始化时就触发错误逻辑。
  3. 通过document.querySelector直接查询DOM获取图表实例的方式不可靠,不符合React组件生命周期规范。

修复方案

  1. 在父组件中创建ref,分别绑定到两个图表组件上,用于安全获取Highcharts实例。
  2. 修改事件处理逻辑:通过点击事件参数获取当前系列名称,在另一个图表中找到同名系列并同步其显示状态。
  3. 将事件处理函数作为引用传递给legendItemClick,避免初始化时执行。
  4. 仅在显示图例的图表上绑定点击事件。

修改后的完整代码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 00:17:01