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

React集成Highcharts出现Maximum update depth exceeded错误求助

问题描述

在React项目中使用Highcharts构建时间序列图表时,图表加载和窗口resize操作会触发函数更新自定义Tooltip的位置及图表最后一项的时间戳,但目前出现了Maximum update depth exceeded(更新深度超过最大值)错误。

错误截图:
错误截图

相关代码
import React, { useState } from 'react';
import Highcharts from 'highcharts';
import HighchartsReact from 'highcharts-react-official';
import sampleData from './output.json';
import { Tooltip } from './components/Tooltip.jsx';
import './App.css';

function App() {
  const [left, setLeft] = useState(0);
  const [showToolTip, toggleToolTip] = useState(true);
  const [timeStamp, setTimeStamp] = useState(null);

  const getColor = (color) => {
    switch (color) {
      case 'NORMAL':
        return 'red';
      case 'MISSING':
        return 'grey';
      case 'WARNING':
        return 'blue';
      default:
        return 'blue';
    }
  };

  // Extract timeline data
  var timelineData = sampleData.data;

  // Format data for Highcharts
  var chartData = timelineData.map(function (entry) {
    return {
      x: new Date(entry.timestamp),
      y: 10, // Fixed y-value
      color: getColor(entry.status),
    };
  });

  const options = {
    credits: {
      enabled: false,
    },
    chart: {
      type: 'column',
      height: '140px',
      zoomType: 'x',
      resetZoomButton: {
        position: {
          align: 'right', // right by default
          verticalAlign: 'top',
          x: -10,
          y: 10,
        },
        relativeTo: 'chart',
      },
      marginTop: '40',
      events: {
        load: function () {
          const chart = this;
          var points = this.series[0].points;
          const lastItem = points[points.length - 1];
          setLeft(chart.plotWidth);
          setTimeStamp(lastItem.x);
        },
        redraw: function () {
          const chart = this;
          var points = this.series[0].points;
          const lastItem = points[points.length - 1];
          setLeft(chart.plotWidth);
          setTimeStamp(lastItem.x);
        },
      },
    },
    title: {
      text: null, // To Remove the tile
    },
    xAxis: {
      minPadding: 0,
      maxPadding: 0,
      dateTimeLabelFormats: {
        hour: '%I %p', // Format hours like "12 AM", "1 AM", etc.
      },
      events: {
        setExtremes: function (e) {
          console.log(e);
          if (typeof e.min == 'undefined' && typeof e.max == 'undefined') {
            // setLeft(e.target.len);
            console.log('e', e.target.len);
          } else {
            // setLeft(e.target.len);
            console.log('e', e.target.len);
          }
        },
      },
      type: 'datetime',
      title: {
        text: null, // To Remove the x axis tile
      },
    },
    yAxis: {
      visible: false,
      title: {
        text: null, // To Remove the y axis title
      },
    },
    tooltip: false,
    plotOptions: {
      series: {
        turboThreshold: 2000,
        groupPadding: 0,
        pointPadding: 0,
        showInLegend: false,
        marker: {
          enabled: true, // Disable markers
        },
        cursor: 'pointer',
        point: {
          events: {
            click: function (x) {
              setLeft(x.chartX);
              setTimeStamp(x.point.x);
              toggleToolTip(true);
              // alert("Category: " + this.category);
            },
          },
        },
      },
    },
    series: [
      {
        name: '',
        showInLegend: false,
        data: chartData,
        dataLabels: {
          enabled: true,
          formatter: function () {
            return this.point.cause;
          },
        },
      },
    ],
  };

  return (
    <div className="App">
      <div id="container">
        <HighchartsReact highcharts={Highcharts} options={options} />
        <Tooltip left={left} timeStamp={timeStamp} showToolTip={showToolTip} />
      </div>
    </div>
  );
}

export default App;
问题原因与解决方案

核心原因

出现无限重渲染循环:

  1. 调用setLeft/setTimeStamp触发React组件重渲染,每次重渲染都会重新创建options对象
  2. HighchartsReact组件检测到options引用变化,会重新渲染Highcharts图表
  3. 图表重新渲染触发load/redraw事件,再次调用setLeft/setTimeStamp,形成闭环循环

解决步骤

1. 稳定options引用

用useMemo缓存options,避免每次重渲染都生成新对象,仅当依赖数据变化时才更新:

import { useState, useMemo } from 'react';

// ... 其他代码不变 ...

const options = useMemo(() => ({
  credits: { enabled: false },
  chart: {
    type: 'column',
    height: '140px',
    zoomType: 'x',
    resetZoomButton: {
      position: { align: 'right', verticalAlign: 'top', x: -10, y: 10 },
      relativeTo: 'chart',
    },
    marginTop: '40',
    events: {
      load: function () {
        const chart = this;
        const lastItem = this.series[0].points.at(-1);
        setLeft(chart.plotWidth);
        setTimeStamp(lastItem.x);
      },
      redraw: function () {
        const chart = this;
        const lastItem = this.series[0].points.at(-1);
        setLeft(chart.plotWidth);
        setTimeStamp(lastItem.x);
      },
    },
  },
  // 原options的其他配置项...
}), [chartData]);

2. 避免不必要的状态更新

在load和redraw事件中,先对比新值与当前状态,仅当值不同时才更新状态,同时用useRef解决闭包缓存问题:

import { useState, useMemo, useRef, useEffect } from 'react';

function App() {
  const [left, setLeft] = useState(0);
  const [timeStamp, setTimeStamp] = useState(null);
  const leftRef = useRef(left);
  const timeStampRef = useRef(timeStamp);

  // 同步状态到ref
  useEffect(() => {
    leftRef.current = left;
    timeStampRef.current = timeStamp;
  }, [left, timeStamp]);

  // ... 其他代码不变 ...

  const options = useMemo(() => ({
    // ... 其他配置 ...
    chart: {
      // ... 其他配置 ...
      events: {
        load: function () {
          const chart = this;
          const lastItem = this.series[0].points.at(-1);
          const newLeft = chart.plotWidth;
          const newTimeStamp = lastItem.x;
          if (newLeft !== leftRef.current) setLeft(newLeft);
          if (newTimeStamp !== timeStampRef.current) setTimeStamp(newTimeStamp);
        },
        redraw: function () {
          const chart = this;
          const lastItem = this.series[0].points.at(-1);
          const newLeft = chart.plotWidth;
          const newTimeStamp = lastItem.x;
          if (newLeft !== leftRef.current) setLeft(newLeft);
          if (newTimeStamp !== timeStampRef.current) setTimeStamp(newTimeStamp);
        },
      },
    },
    // ... 其他配置 ...
  }), [chartData]);
}

3. 优化图表数据生成

用useMemo缓存chartData,避免每次重渲染都重新映射数据:

const chartData = useMemo(() => {
  return timelineData.map(entry => ({
    x: new Date(entry.timestamp),
    y: 10,
    color: getColor(entry.status),
  }));
}, [timelineData]);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 12:51:08