React中Chart.js双折线图标签乱序及折线异常问题排查
问题:Chart.js折线图时间轴乱序、折线错位
我在React项目里用Chart.js展示REST接口返回的数据,数据格式没问题,但渲染图表时一条折线正常,另一条呈锯齿状来回波动,而且X轴标签没按时间排序。之前试过的时间轴配置已经注释了,请问怎么让标签按时间排序,同时让两条折线的点对应正确的标签?

现有代码
import React, { useState, useMemo } from "react" import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Tooltip, Legend, TimeScale } from 'chart.js'; import { Line } from 'react-chartjs-2'; import 'chartjs-adapter-date-fns'; export const PlayerImprovementChart = (props) => { const [labels, setLabels] = useState([]); const [redScores] = useState([]); const [whiteScores] = useState([]); const [data] = useState({ datasets: [ { label: 'Red Pad', data: redScores, borderColor: 'rgb(255, 99, 132)', backgroundColor: 'rgba(255, 99, 132, 0.5)', }, { label: 'White Pad', data: whiteScores, borderColor: 'rgb(255, 255, 255)', backgroundColor: 'rgba(255, 255, 255, .5)', }, ], }) ChartJS.register( CategoryScale, LinearScale, PointElement, LineElement, Tooltip, Legend, TimeScale ); const options = { animation: false, responsive: true, plugins: { legend: { position: 'top', labels: { color: 'white' }, }, }, scales: { y: { ticks: { color: 'white' }, }, x: { ticks: { color: 'white' }, scaleLabel: { display: true, labelString: 'Date', }, // type: 'time', // time: { // parser: 'yyyy-MM-ddHH:mm:ss.SSS+SS:SS', // unit: 'day', // displayFormats: { // day: 'MM-dd-yyyy' // } // } }, }, parsing: { xAxisKey: 'date', yAxisKey: 'score' } }; function clearArray(array) { if(array != null) { if(typeof array === Object) { array = null; } else { while (array.length > 0) { array.pop(); } } } } //set the chart dates useMemo(() => { //clear arrays on reload clearArray(redScores); clearArray(whiteScores); if (props.redScores != null) { if (Object.keys(props.redScores).length > 0) { Object.keys(props.redScores).forEach(function (date) { if (props.redScores[date] !== 999 && props.redScores[date] !== 0) { let score = props.redScores[date]; redScores.push({ date, score }); } }); } } if (props.whiteScores != null) { if (Object.keys(props.whiteScores).length > 0) { Object.keys(props.whiteScores).forEach(function (date) { if (props.whiteScores[date] !== 999 && props.whiteScores[date] !== 0) { let score = props.whiteScores[date]; whiteScores.push({ date, score }); } }); } } }, [props.dates, props.dateTo, props.dateFrom, props.redScores, props.whiteScores, labels, redScores, whiteScores]); return ( <div className="container"> <Line options={options} data={data} /> </div> ) }; export default PlayerImprovementChart;
编辑补充:
- Red Scores:键为ISO格式时间字符串,值为分数(示例:
"2024-05-20T12:34:56.789+00:00": 85) - White Scores:数据结构与Red Scores一致,包含时间键和对应分数
解决方案
关键问题分析
- 数据顺序错误:
Object.keys()返回的是字符串字典序,不是时间顺序,导致X轴标签乱序、折线点错位 - 状态更新错误:直接修改
useState生成的数组(redScores.push())不会触发组件重新渲染 - 时间轴配置错误:之前的时间解析格式不匹配ISO 8601时间字符串,导致TimeScale无法正常工作
修改后的完整代码
import React, { useState, useMemo } from "react" import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Tooltip, Legend, TimeScale } from 'chart.js'; import { Line } from 'react-chartjs-2'; import 'chartjs-adapter-date-fns'; export const PlayerImprovementChart = (props) => { // 修正状态定义,添加setter用于更新 const [redScores, setRedScores] = useState([]); const [whiteScores, setWhiteScores] = useState([]); // 让data依赖状态变化,确保数据更新后图表重新渲染 const data = useMemo(() => ({ datasets: [ { label: 'Red Pad', data: redScores, borderColor: 'rgb(255, 99, 132)', backgroundColor: 'rgba(255, 99, 132, 0.5)', }, { label: 'White Pad', data: whiteScores, borderColor: 'rgb(255, 255, 255)', backgroundColor: 'rgba(255, 255, 255, .5)', }, ], }), [redScores, whiteScores]); ChartJS.register( CategoryScale, LinearScale, PointElement, LineElement, Tooltip, Legend, TimeScale ); const options = { animation: false, responsive: true, plugins: { legend: { position: 'top', labels: { color: 'white' }, }, }, scales: { y: { ticks: { color: 'white' }, }, x: { ticks: { color: 'white' }, scaleLabel: { display: true, labelString: 'Date', }, type: 'time', // 启用时间轴 time: { parser: 'yyyy-MM-dd\'T\'HH:mm:ss.SSSXXX', // 匹配ISO 8601时间格式 unit: 'day', displayFormats: { day: 'MM-dd-yyyy' } } }, }, parsing: { xAxisKey: 'date', yAxisKey: 'score' } }; // 修正数据处理逻辑:过滤无效值+时间排序+状态更新 useMemo(() => { // 处理红榜数据 const processedRed = props.redScores ? Object.keys(props.redScores) .filter(date => props.redScores[date] !== 999 && props.redScores[date] !== 0) .sort((a, b) => new Date(a) - new Date(b)) // 按时间升序排序 .map(date => ({ date, score: props.redScores[date] })) : []; // 处理白榜数据,同红榜逻辑 const processedWhite = props.whiteScores ? Object.keys(props.whiteScores) .filter(date => props.whiteScores[date] !== 999 && props.whiteScores[date] !== 0) .sort((a, b) => new Date(a) - new Date(b)) .map(date => ({ date, score: props.whiteScores[date] })) : []; // 更新状态,触发组件重新渲染 setRedScores(processedRed); setWhiteScores(processedWhite); }, [props.redScores, props.whiteScores]); // 只保留必要的依赖项 return ( <div className="container"> <Line options={options} data={data} /> </div> ) }; export default PlayerImprovementChart;
修改点说明
- 数据排序:用
new Date(a) - new Date(b)将时间键转为Date对象后排序,确保数据按时间顺序排列 - 状态更新:通过
setRedScores和setWhiteScores更新状态,替代直接修改数组的错误写法,保证组件能感知数据变化 - 时间轴配置:修正时间解析格式为
yyyy-MM-dd'T'HH:mm:ss.SSSXXX,完美匹配ISO 8601时间字符串 - 依赖优化:简化
useMemo的依赖项,避免不必要的重复计算 - 移除无效代码:删除逻辑错误的
clearArray函数,直接生成新数组替代手动清空操作
内容的提问来源于stack exchange,提问作者Enter Strandman
相关产品推荐
相关产品推荐

