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

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一致,包含时间键和对应分数

解决方案

关键问题分析

  1. 数据顺序错误:Object.keys()返回的是字符串字典序,不是时间顺序,导致X轴标签乱序、折线点错位
  2. 状态更新错误:直接修改useState生成的数组(redScores.push())不会触发组件重新渲染
  3. 时间轴配置错误:之前的时间解析格式不匹配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;

修改点说明

  1. 数据排序:用new Date(a) - new Date(b)将时间键转为Date对象后排序,确保数据按时间顺序排列
  2. 状态更新:通过setRedScores和setWhiteScores更新状态,替代直接修改数组的错误写法,保证组件能感知数据变化
  3. 时间轴配置:修正时间解析格式为yyyy-MM-dd'T'HH:mm:ss.SSSXXX,完美匹配ISO 8601时间字符串
  4. 依赖优化:简化useMemo的依赖项,避免不必要的重复计算
  5. 移除无效代码:删除逻辑错误的clearArray函数,直接生成新数组替代手动清空操作

内容的提问来源于stack exchange,提问作者Enter Strandman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 22:43:13