diff --git a/src/components/charts/TimeSeriesChart.tsx b/src/components/charts/TimeSeriesChart.tsx index 8788e55..40d38a7 100644 --- a/src/components/charts/TimeSeriesChart.tsx +++ b/src/components/charts/TimeSeriesChart.tsx @@ -3,7 +3,7 @@ import ReactECharts, { EChartsOption } from 'echarts-for-react'; import { format } from 'date-fns'; import { parseDate } from '../../lib/daterange'; -import React, { useState, useEffect, memo, useRef } from 'react'; +import React, { useState, useEffect, memo, useRef, useCallback } from 'react'; import { getBackendSrv } from '@grafana/runtime'; import { getBackendUrl } from '../../constants'; const formatNumber = (num: number) => { @@ -25,7 +25,13 @@ function lessThan3Day(startTs: number, endTs: number) { const whoAmI = "timeseries-chart"; // Create the base component -function TimeSeriesChartBase(params: { +function TimeSeriesChartBase({ + startDate, + endDate, + whoChanged, + currentTable, + onDateRangeSelect +}: { startDate: string endDate: string whoChanged: string @@ -43,17 +49,39 @@ function TimeSeriesChartBase(params: { endTs: 0 }); + // Add throttling timer ref + const throttleTimerRef = useRef(null); + + // Throttled version of onDateRangeSelect + const throttledDateRangeSelect = useCallback((newStartDate: string, newEndDate: string) => { + if (throttleTimerRef.current) { + clearTimeout(throttleTimerRef.current); + } + + throttleTimerRef.current = setTimeout(() => { + onDateRangeSelect?.(newStartDate, newEndDate, whoAmI); + }, 150); // 150ms throttle + }, [onDateRangeSelect]); + + // Cleanup throttle timer useEffect(() => { + return () => { + if (throttleTimerRef.current) { + clearTimeout(throttleTimerRef.current); + } + }; + }, []); - if (params.currentTable == null) { + useEffect(() => { + if (currentTable == null) { return; } - if (params.currentTable === "") { + if (currentTable === "") { return; } - const startTs = parseDate(params.startDate); - const endTs = parseDate(params.endDate); + const startTs = parseDate(startDate); + const endTs = parseDate(endDate); dataRangeRef.current = { startTs, endTs }; @@ -65,7 +93,7 @@ function TimeSeriesChartBase(params: { headers: { 'Content-Type': 'application/json' }, - data: JSON.stringify({ query: "", startDate: startTs, endDate: endTs, tableName: params.currentTable }), + data: JSON.stringify({ query: "", startDate: startTs, endDate: endTs, tableName: currentTable }), showErrorAlert: false }).subscribe({ next: (resp) => { @@ -82,8 +110,7 @@ function TimeSeriesChartBase(params: { } }); - - }, [params.startDate, params.endDate, params.currentTable]); + }, [startDate, endDate, currentTable]); if (loading) { return null; @@ -258,11 +285,7 @@ function TimeSeriesChartBase(params: { dataRangeRef.current = { startTs, endTs }; - params.onDateRangeSelect?.( - startDate, - endDate, - whoAmI - ); + throttledDateRangeSelect(startDate, endDate); } } }} @@ -271,32 +294,46 @@ function TimeSeriesChartBase(params: { ); } -// Create a custom comparison function for memo +// Optimize the comparison function for memo const arePropsEqual = (prevProps: any, nextProps: any) => { - const prevStartTs = parseDate(prevProps.startDate); - const prevEndTs = parseDate(prevProps.endDate); - const nextStartTs = parseDate(nextProps.startDate); - const nextEndTs = parseDate(nextProps.endDate); + // Quick equality check for identical objects + if (prevProps === nextProps) { + return true; + } + // If table changed, must re-render if (prevProps.currentTable !== nextProps.currentTable) { - return false + return false; } + // If change came from another component, must re-render if (nextProps.whoChanged !== whoAmI) { return false; } - // If the new date range is within the old one, don't re-render + const prevStartTs = parseDate(prevProps.startDate); + const prevEndTs = parseDate(prevProps.endDate); + const nextStartTs = parseDate(nextProps.startDate); + const nextEndTs = parseDate(nextProps.endDate); + + // If dates are exactly the same, don't re-render + if (prevStartTs === nextStartTs && prevEndTs === nextEndTs) { + return true; + } + + // If new range is within old range, don't re-render if (nextStartTs >= prevStartTs && nextEndTs <= prevEndTs) { - return true; // props are equal, don't re-render + return true; } - // If the date range is exactly the same, don't re-render - if (prevStartTs === nextStartTs && prevEndTs === nextEndTs) { + // If the difference is less than 1 second, don't re-render + const startDiff = Math.abs(nextStartTs - prevStartTs); + const endDiff = Math.abs(nextEndTs - prevEndTs); + if (startDiff < 1000 && endDiff < 1000) { return true; } - return false; // props are different, allow re-render + return false; }; // Export the memoized component