Refactor: Defer state updates and simplify query time handling in PanelPerformanceMetrics

This commit refines the PanelPerformanceMetrics component by deferring state updates to avoid React warnings during rendering. It also simplifies the handling of query time by removing the fake timer logic, directly using the last query time for display. Additionally, the polling for profiling state changes is adjusted to run once, improving performance and reducing unnecessary re-renders.
This commit is contained in:
David Kaltschmidt
2025-12-05 17:27:23 +01:00
parent 7bea99a54b
commit b5320defd1
2 changed files with 19 additions and 49 deletions
+1 -1
View File
@@ -4677,4 +4677,4 @@
"count": 1
}
}
}
}
@@ -38,10 +38,13 @@ export class PanelPerformanceMetrics extends SceneObjectBase<PanelPerformanceMet
const aggregator = getDashboardAnalyticsAggregator();
const panelIdStr = String(panelId);
// Subscribe to metrics updates - callback will be called with initial metrics if available
// Subscribe to metrics updates - defer initial callback to avoid setState during render
this._subs.add(
aggregator.subscribeToPanelMetrics(panelIdStr, (updatedMetrics) => {
this.setState({ metrics: updatedMetrics });
// Defer state update to avoid React warning about updating during render
setTimeout(() => {
this.setState({ metrics: updatedMetrics });
}, 0);
})
);
};
@@ -68,17 +71,21 @@ function PanelPerformanceMetricsRenderer({ model }: SceneComponentProps<PanelPer
const panel = model.getPanel();
const styles = useStyles2(getStyles);
const { metrics } = model.useState();
const [fakeQueryTime, setFakeQueryTime] = useState(0);
const [isProfilingEnabled, setIsProfilingEnabled] = useState(isPanelProfilingEnabled());
const intervalRef = useRef<NodeJS.Timeout | null>(null);
const startTimeRef = useRef<number | null>(null);
const profilingStateRef = useRef(isProfilingEnabled);
// Update ref when state changes
useEffect(() => {
profilingStateRef.current = isProfilingEnabled;
}, [isProfilingEnabled]);
// Watch for profiling state changes (when toggled via hotkey)
useEffect(() => {
// Poll for profiling state changes - this allows the component to react when profiling is toggled
const checkProfilingState = () => {
const currentState = isPanelProfilingEnabled();
if (currentState !== isProfilingEnabled) {
// Compare against ref to avoid stale closure
if (currentState !== profilingStateRef.current) {
setIsProfilingEnabled(currentState);
}
};
@@ -88,7 +95,7 @@ function PanelPerformanceMetricsRenderer({ model }: SceneComponentProps<PanelPer
const interval = setInterval(checkProfilingState, 100);
return () => clearInterval(interval);
}, [isProfilingEnabled]);
}, []); // Empty dependency array - polling should run once and persist
// Get last operation times (most recent operation in each array)
const lastQueryTime =
@@ -104,44 +111,7 @@ function PanelPerformanceMetricsRenderer({ model }: SceneComponentProps<PanelPer
? metrics.transformationOperations[metrics.transformationOperations.length - 1].duration
: 0;
// Manage fake timer for query time
useEffect(() => {
// If we have a real query time, stop the fake timer
if (lastQueryTime > 0) {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
setFakeQueryTime(0);
startTimeRef.current = null;
return;
}
// If no query operations yet, start the fake timer
if (metrics && metrics.queryOperations.length === 0) {
// Start timer if not already running
if (!intervalRef.current) {
startTimeRef.current = Date.now();
intervalRef.current = setInterval(() => {
if (startTimeRef.current) {
setFakeQueryTime(Date.now() - startTimeRef.current);
}
}, 166); // Update every 166ms for smooth counting
}
}
// Cleanup on unmount
return () => {
if (intervalRef.current) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
};
}, [lastQueryTime, metrics]);
// Use fake query time if real one is 0
const displayQueryTime = lastQueryTime > 0 ? lastQueryTime : fakeQueryTime;
const lastTotalTime = displayQueryTime + lastRenderTime + lastTransformTime;
const lastTotalTime = lastQueryTime + lastRenderTime + lastTransformTime;
// Don't render if panel is not available
if (!panel) {
@@ -169,7 +139,7 @@ function PanelPerformanceMetricsRenderer({ model }: SceneComponentProps<PanelPer
const tooltipContent = (
<div>
{renderMetricRow('Query', displayQueryTime)}
{renderMetricRow('Query', lastQueryTime)}
{hasTransformations && renderMetricRow('Transform', lastTransformTime)}
{renderMetricRow('Render', lastRenderTime)}
<div style={{ marginTop: '8px', borderTop: '1px solid rgba(255,255,255,0.1)', paddingTop: '8px' }}>
@@ -184,14 +154,14 @@ function PanelPerformanceMetricsRenderer({ model }: SceneComponentProps<PanelPer
// Otherwise, only show non-zero metrics
const metricsText = isProfilingEnabled
? [
`Q:${formatDuration(displayQueryTime)}`,
`Q:${formatDuration(lastQueryTime)}`,
hasTransformations && `T:${formatDuration(lastTransformTime)}`,
`R:${formatDuration(lastRenderTime)}`,
]
.filter(Boolean)
.join(' ')
: [
displayQueryTime > 0 && `Q:${formatDuration(displayQueryTime)}`,
lastQueryTime > 0 && `Q:${formatDuration(lastQueryTime)}`,
lastTransformTime > 0 && `T:${formatDuration(lastTransformTime)}`,
lastRenderTime > 0 && `R:${formatDuration(lastRenderTime)}`,
]