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> ); } } }
问题核心错误
- 数据传递逻辑完全错误:修改后
getAlertTrendGraph和complianceTrendGraph返回的是LineChart组件数组,但在render中又将这些组件数组传给LineChart的seriesData属性——而seriesData要求的是数值数组的数组,不是React组件。 - 组件复用逻辑错误:自定义
LineChart应该是完整的图表容器,每个Tab只需要渲染一个LineChart,而非多个。原来的逻辑是在父容器中管理多系列,现在错误地拆分成了多个LineChart实例。 - 丢失关键配置:修改后完全移除了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' }
相关产品推荐
相关产品推荐

