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

Highcharts自定义折线图组件数据不渲染问题求助

Highcharts自定义LineChart组件替换后无法实现折线图的问题解决

问题背景

我是Highcharts新手,想要实现两个折线图,为此创建了自定义LineChart组件替换现有库,组件接口如下:

export interface LineChartProps {
  title?: string;
  xTitle?: string;
  yTitle?: string;
  chartWidth?: number;
  chartHeight?: number;
  seriesData: any[][];
  seriesColor: string[];
  gridLine?:boolean;
  name?: any;
}  

修改前后代码对比

修改前代码

private getAlertTrendGraph = (alertTrendData: TrendType) => {
        /* Reading in the Alert Trend API Data and constructing the Chart Lines. */
        let result = ['Low', 'Medium', 'High'].map((keyValue, index) => {
            if (alertTrendData.values[keyValue] !== undefined) {

                let alertTrendValue = alertTrendData.values[keyValue].map((data) => {
                    return (parseInt(data, 10));
                });
                let color = '#FFFFFF';
                if (keyValue === 'Low') {
                    color = '#27AEF0';
                }
                else if (keyValue === 'Medium') {
                    color = '#FFC627';
                }
                else if (keyValue === 'High') {
                    color = '#ED3124';
                }
                return (
                    <Chart.Line
                        key={'AlertTrend' + index}
                        name={keyValue}
                        data={alertTrendValue}
                        color={color}
                    />
                );
            } else {
                return;
            }
        });
        return result;

    }

    private complianceTrendGraph = (complianceTrendData: TrendType) => {
        /* Reading in the Compliance Trend API Data and constructing the Chart Lines. */
        let result = complianceTrendData.keys.map((keyValue, index) => {
            let complianceTrendValue = complianceTrendData.values[keyValue].map((data) => {
                return (parseFloat(data));
            });
            return (
                <Chart.Line
                    key={'ComplianceTrend' + index}
                    name={keyValue}
                    data={complianceTrendValue}
                />
            );
        });
        return result;
    }

    convertUTCToLocalDate(timestamps:Array<string>){
        let localTimestamps:any=[];
        timestamps.map((dateTime)=>{
        const sp=dateTime.toString().split(" ");
        if(sp[0]! && sp[1]!){
        const date = new Date(sp[0]);
        const month =((date.getMonth()+1)<10)?"0"+(date.getMonth()+1):(date.getMonth()+1);
        const day =(date.getDate()<10)?"0"+date.getDate():date.getDate();
        let str1=date.getFullYear()+'-' + month+'-'+day+'T'+sp[1];
        let ISOdate=new Date(str1+'Z');
        const utc=moment.utc(ISOdate);
        const local=utc.local();
        localTimestamps.push(local.format('DD MMM YYYY').toString());
    }
  });
  return localTimestamps;
}  

    onTabChange = (index: number) => {
        const store = this.props.dashboardStore!;
        if(index === 1 && !this.alertTrendGraph[0]) {
            store.updateLoaderCount();
            store.postDataQuery('AlertTrend', this.category, this.data,'');
        }
    }    

    componentWillUnmount() {
        this.alertTrendGraph = undefined;
    }

    render() {
        const store = this.props.dashboardStore!;
        let alertTrendData = toJS(store.alertTrend!);
        let complianceTrendData = toJS(store.complianceTrend!);
        if (alertTrendData.keys === undefined || complianceTrendData.keys === undefined) {
            return (
                <div />
            );
        } else {
            alertTrendData.timestamps = this.convertUTCToLocalDate(alertTrendData.timestamps);
            complianceTrendData.timestamps = this.convertUTCToLocalDate(complianceTrendData.timestamps);
            this.alertTrendGraph = this.getAlertTrendGraph(alertTrendData);
            let complianceTrendGraph = this.complianceTrendGraph(complianceTrendData);

            return (
                <div className="systemview-graph">
                    <Tab defaultActiveIndex={0} onTabChange={(activeIndex)=>(this.onTabChange(activeIndex))} >
                        <Tab.Pane title="Score Trend">
                            <Chart animation={false} height="350" key="Chart2">
                                {complianceTrendGraph}
                                <Chart.YAxis
                                    showGridLines={true}
                                    gridLineColor="#202020"
                                    labels={{ enabled: false }}
                                    tickLength={5}
                                    tickWidth={1}
                                    lineWidth={1}
                                    lineColor="white"
                                    min={0.0}
                                    max={100.0}
                                    title={{text: "Percent(%)", style: { color: 'white', fontSize: '15px' } }}
                                />
                                <Chart.XAxis
                                    showGridLines={false}
                                    labels={{ style: { color: 'white', fontSize: '15px' } }}
                                    tickLength={10}
                                    tickWidth={1}
                                    tickColor="white"
                                    categories={complianceTrendData.timestamps}

                                />
                                <Chart.Legend
                                    align="left"
                                    verticalAlign="bottom"
                                />
                            </Chart>
                        </Tab.Pane>
                        <Tab.Pane title="Alert Trend">
                            <Chart animation={false} height="350" key="Chart1">
                                {this.alertTrendGraph}
                                <Chart.YAxis
                                    showGridLines={true}
                                    gridLineColor="#202020"
                                    labels={{ enabled: false }}
                                    tickLength={5}
                                    tickWidth={1}
                                    lineWidth={1}
                                    lineColor="white"
                                />

                                <Chart.XAxis
                                    showGridLines={false}
                                    labels={{ style: { color: 'white', fontSize: '15px' } }}
                                    tickLength={10}
                                    tickWidth={1}
                                    tickColor="white"
                                    categories={alertTrendData.timestamps}
                                />
                                <Chart.Legend
                                    align="left"
                                    verticalAlign="bottom"
                                />
                            </Chart>
                        </Tab.Pane>

                    </Tab>
                </div>
            );
        }

修改后代码

private getAlertTrendGraph = (alertTrendData: TrendType) => {
        /* Reading in the Alert Trend API Data and constructing the Chart Lines. */
        let result = ['Low', 'Medium', 'High'].map((keyValue, index) => {
            if (alertTrendData.values[keyValue] !== undefined) {

                let alertTrendValue = alertTrendData.values[keyValue].map((data) => {
                    return (parseInt(data, 10));
                });
                let color = '#FFFFFF';
                if (keyValue === 'Low') {
                    color = '#27AEF0';
                }
                else if (keyValue === 'Medium') {
                    color = '#FFC627';
                }
                else if (keyValue === 'High') {
                    color = '#ED3124';
                }
                return (
                    <LineChart
                        key={'AlertTrend' + index}
                        name={keyValue}
                        seriesData={[alertTrendValue]}
                        seriesColor={[color]} 
                        />
                );
            } else {
                return;
            }
        });
        return result;

    }

    private complianceTrendGraph = (complianceTrendData: TrendType) => {
        /* Reading in the Compliance Trend API Data and constructing the Chart Lines. */
        let result = complianceTrendData.keys.map((keyValue, index) => {
            let complianceTrendValue = complianceTrendData.values[keyValue].map((data) => {
                return (parseFloat(data));
            });
            return (
                <LineChart
                    key={'ComplianceTrend' + index}
                    name={keyValue}
                    seriesData={[complianceTrendValue]} 
                    seriesColor={['#FFFFFF']}                
                />
            );
        });
        return result;
    }

    convertUTCToLocalDate(timestamps:Array<string>){
        let localTimestamps:any=[];
        timestamps.map((dateTime)=>{
        const sp=dateTime.toString().split(" ");
        if(sp[0]! && sp[1]!){
        const date = new Date(sp[0]);
        const month =((date.getMonth()+1)<10)?"0"+(date.getMonth()+1):(date.getMonth()+1);
        const day =(date.getDate()<10)?"0"+date.getDate():date.getDate();
        let str1=date.getFullYear()+'-' + month+'-'+day+'T'+sp[1];
        let ISOdate=new Date(str1+'Z');
        const utc=moment.utc(ISOdate);
        const local=utc.local();
        localTimestamps.push(local.format('DD MMM YYYY').toString());
    }
  });
  return localTimestamps;
}  

    onTabChange = (index: number) => {
        const store = this.props.dashboardStore!;
        if(index === 1 && !this.alertTrendGraph[0]) {
            store.updateLoaderCount();
            store.postDataQuery('AlertTrend', this.category, this.data,'');
        }
    }    

    componentWillUnmount() {
        this.alertTrendGraph = undefined;
    }

    render() {
        const store = this.props.dashboardStore!;
        let alertTrendData = toJS(store.alertTrend!);
        let complianceTrendData = toJS(store.complianceTrend!);
        if (alertTrendData.keys === undefined || complianceTrendData.keys === undefined) {
            return (
                <div />
            );
        } else {
            alertTrendData.timestamps = this.convertUTCToLocalDate(alertTrendData.timestamps);
            complianceTrendData.timestamps = this.convertUTCToLocalDate(complianceTrendData.timestamps);
            this.alertTrendGraph = this.getAlertTrendGraph(alertTrendData);
            let complianceTrendGraph = this.complianceTrendGraph(complianceTrendData);

            return (
                <div className="systemview-graph">
                    <Tab defaultActiveIndex={0} onTabChange={(activeIndex)=>(this.onTabChange(activeIndex))} >
                        <Tab.Pane title="Score Trend">
                                <LineChart 
                                seriesData={[complianceTrendGraph]} 
                                seriesColor={['#1792E5']} />
                              
                        </Tab.Pane>
                        <Tab.Pane title="Alert Trend">
                            <LineChart 
                            seriesData={[this.alertTrendGraph]} 
                            seriesColor={['#1792E5']} />
                        </Tab.Pane>

                    </Tab>
                </div>
            );
        }
    }
}

问题核心错误

  1. 数据传递逻辑完全错误:修改后getAlertTrendGraph和complianceTrendGraph返回的是LineChart组件数组,但在render中又将这些组件数组传给LineChart的seriesData属性——而seriesData要求的是数值数组的数组,不是React组件。
  2. 组件复用逻辑错误:自定义LineChart应该是完整的图表容器,每个Tab只需要渲染一个LineChart,而非多个。原来的逻辑是在父容器中管理多系列,现在错误地拆分成了多个LineChart实例。
  3. 丢失关键配置:修改后完全移除了X轴时间戳分类、Y轴范围/标题、图例等核心配置,导致图表无法正确渲染。

修复方案

第一步:重构数据处理函数,返回系列数据而非组件

private getAlertTrendSeries = (alertTrendData: TrendType) => {
  return ['Low', 'Medium', 'High']
    .filter(keyValue => alertTrendData.values[keyValue] !== undefined)
    .map((keyValue) => {
      const data = alertTrendData.values[keyValue].map((item) => parseInt(item, 10));
      let color = '#FFFFFF';
      if (keyValue === 'Low') color = '#27AEF0';
      else if (keyValue === 'Medium') color = '#FFC627';
      else if (keyValue === 'High') color = '#ED3124';
      return { name: keyValue, data, color };
    });
}

private getComplianceTrendSeries = (complianceTrendData: TrendType) => {
  return complianceTrendData.keys.map((keyValue) => {
    const data = complianceTrendData.values[keyValue].map((item) => parseFloat(item));
    return { name: keyValue, data, color: '#FFFFFF' };
  });
}

第二步:修改render函数,正确传递所有配置给单个LineChart

render() {
  const store = this.props.dashboardStore!;
  let alertTrendData = toJS(store.alertTrend!);
  let complianceTrendData = toJS(store.complianceTrend!);
  
  if (alertTrendData.keys === undefined || complianceTrendData.keys === undefined) {
    return <div />;
  }
  
  const localAlertTimestamps = this.convertUTCToLocalDate(alertTrendData.timestamps);
  const localComplianceTimestamps = this.convertUTCToLocalDate(complianceTrendData.timestamps);
  
  const alertSeries = this.getAlertTrendSeries(alertTrendData);
  const complianceSeries = this.getComplianceTrendSeries(complianceTrendData);

  return (
    <div className="systemview-graph">
      <Tab defaultActiveIndex={0} onTabChange={this.onTabChange}>
        <Tab.Pane title="Score Trend">
          <LineChart
            title="Score Trend"
            yTitle="Percent(%)"
            chartHeight={350}
            seriesData={complianceSeries.map(s => s.data)}
            seriesColor={complianceSeries.map(s => s.color)}
            xCategories={localComplianceTimestamps}
            gridLine={true}
          />
        </Tab.Pane>
        <Tab.Pane title="Alert Trend">
          <LineChart
            title="Alert Trend"
            chartHeight={350}
            seriesData={alertSeries.map(s => s.data)}
            seriesColor={alertSeries.map(s => s.color)}
            xCategories={localAlertTimestamps}
            gridLine={true}
          />
        </Tab.Pane>
      </Tab>
    </div>
  );
}

第三步:补全LineChart组件内部的Highcharts配置

需要在自定义LineChart中添加X/Y轴、图例等逻辑,确保能处理新增的xCategories属性:

import Highcharts from 'highcharts';
import { useEffect, useRef } from 'react';

const LineChart = ({
  title,
  xTitle,
  yTitle,
  chartWidth,
  chartHeight,
  seriesData,
  seriesColor,
  gridLine,
  xCategories,
}: LineChartProps & { xCategories?: string[] }) => {
  const chartRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!chartRef.current) return;

    const chart = Highcharts.chart(chartRef.current, {
      chart: {
        width: chartWidth,
        height: chartHeight,
        backgroundColor: 'transparent'
      },
      title: { text: title, style: { color: 'white' } },
      xAxis: {
        categories: xCategories,
        labels: { style: { color: 'white', fontSize: '15px' } },
        tickLength: 10,
        tickWidth: 1,
        tickColor: 'white',
        showGridLines: false
      },
      yAxis: {
        title: { text: yTitle, style: { color: 'white', fontSize: '15px' } },
        showGridLines: gridLine,
        gridLineColor: '#202020',
        labels: { enabled: false },
        tickLength: 5,
        tickWidth: 1,
        lineWidth: 1,
        lineColor: 'white',
        ...(yTitle === 'Percent(%)' && { min: 0, max: 100 })
      },
      series: seriesData.map((data, index) => ({
        type: 'line',
        data,
        color: seriesColor[index],
        name: '' // 若需要系列名称,可在Props中添加seriesNames数组传递
      })),
      legend: {
        align: 'left',
        verticalAlign: 'bottom',
        itemStyle: { color: 'white' }
相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 08:29:57