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;
问题原因与解决方案
核心原因
出现无限重渲染循环:
- 调用
setLeft/setTimeStamp触发React组件重渲染,每次重渲染都会重新创建options对象 HighchartsReact组件检测到options引用变化,会重新渲染Highcharts图表- 图表重新渲染触发
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
相关产品推荐
相关产品推荐

