React中ECharts双图表缩放状态同步出现回弹问题求助
React中ECharts双折线图缩放同步回弹问题解决
在React项目里用ECharts实现两个折线图,需求是调整其中一个图表的缩放状态时,另一个自动同步到相同级别,但实际操作后图表会回弹到原始位置。
代码示例
LineChart.tsx
export const LineChart = React.forwardRef<ReactECharts, LineChartProps>((props, ref) => { { zoomState, onDataZoom } = props const option: EChartsOption = { dataZoom: [{ type: 'slider', start: zoomState.start, end: zoomState.end, }], } return ( <ReactECharts ref={ref} option={option} onEvents={{ 'datazoom': onDataZoom, }} /> ); });
index.tsx
const [zoomState, setZoomState] = useState<{start: number, end: number}>({ start: 80, end: 100 }); function onDataZoom(params: any) { const newZoom = { start: params.start, end: params.end }; setZoomState(newZoom); } var co2Chart = ( <LineChart ref={co2ChartRef} data={device.deviceData} zoomState={zoomState} onDataZoom={onDataZoom} valueToPlot={'co2'} // ... (其他props) /> ) var tempChart = ( <LineChart ref={tempChartRef} data={device.deviceData} zoomState={zoomState} onDataZoom={onDataZoom} valueToPlot={'temperature'} // ... (其他props) /> )
问题成因观察
- 拖动缩放滑块触发
datazoom事件,调用onDataZoom更新React状态 - React状态更新前,ECharts内部会先重绘图表,但此时状态未更新,图表会恢复到旧的缩放级别
- 旧缩放级别又触发新的
datazoom事件,形成循环,最终导致图表回弹
解决方法
1. 只处理用户操作触发的缩放事件
ECharts的datazoom事件参数里有isFromUser字段,可区分用户手动操作和程序自动更新。修改onDataZoom函数,只响应用户操作:
function onDataZoom(params: any) { // 仅处理用户手动触发的缩放,忽略程序自动更新的情况 if (params.isFromUser) { const newZoom = { start: params.start, end: params.end }; setZoomState(newZoom); } }
2. 缓存图表配置,避免不必要重绘
用useMemo缓存ECharts的option,只有当zoomState变化时才重新生成配置,减少无效重绘:
// LineChart.tsx export const LineChart = React.forwardRef<ReactECharts, LineChartProps>((props, ref) => { const { zoomState, onDataZoom } = props; // 用useMemo缓存option,依赖zoomState变化时才更新 const option = React.useMemo(() => ({ dataZoom: [{ type: 'slider', start: zoomState.start, end: zoomState.end, }], // 其他图表配置... }), [zoomState]); return ( <ReactECharts ref={ref} option={option} onEvents={{ 'datazoom': onDataZoom, }} /> ); });
3. 可选:添加状态更新防抖(按需)
如果仍存在抖动问题,可以给setZoomState添加防抖,避免短时间内多次触发状态更新:
import { debounce } from 'lodash'; const debouncedSetZoomState = React.useCallback( debounce((newZoom) => setZoomState(newZoom), 100), [] ); function onDataZoom(params: any) { if (params.isFromUser) { const newZoom = { start: params.start, end: params.end }; debouncedSetZoomState(newZoom); } }
内容的提问来源于stack exchange,提问作者adjuric
相关产品推荐
相关产品推荐

