From 2e996246823793e3e20ca633f4e4c180d9feb243 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 12 Mar 2026 18:55:37 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20[performance=20improvement]?= =?UTF-8?q?=20dashboard=20backup=20chart=20computation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 💡 What: Wrapped `recentBackupsChart` generation in `SummaryPanel` with a `useMemo` hook. 🎯 Why: Previously, the array map, timestamp formatting, and charting object allocation ran on every single render. The dashboard view runs intervals and uses multiple states, making these re-renders high frequency. 📊 Impact: Prevents continuous object reallocations of the 60 chart bars per repo panel on dashboard tick renders. 🔬 Measurement: Check React profiler on `SummaryPanel` when dashboard re-renders. Co-authored-by: garethgeorge <7906572+garethgeorge@users.noreply.github.com> --- .jules/bolt.md | 3 + .../features/dashboard/SummaryDashboard.tsx | 55 ++++++++++--------- 2 files changed, 32 insertions(+), 26 deletions(-) create mode 100644 .jules/bolt.md diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 00000000..d519d85a --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2024-05-15 - [Refactored summaryDashboard backup chart loop] +**Learning:** React component with high frequency dashboard needs memoization for derived charting state. Specifically the \`recentBackupsChart\` calculation re-ran on every single render. +**Action:** Used \`useMemo\` to prevent recalculation of the backups chart array on every render, this stops recalculating colors, formatting timestamps, and building the chart data repeatedly unless the \`recentBackups\` data changes. diff --git a/webui/src/features/dashboard/SummaryDashboard.tsx b/webui/src/features/dashboard/SummaryDashboard.tsx index 5908e343..0756d9da 100644 --- a/webui/src/features/dashboard/SummaryDashboard.tsx +++ b/webui/src/features/dashboard/SummaryDashboard.tsx @@ -176,33 +176,36 @@ const SummaryPanel = ({ }: { summary: SummaryDashboardResponse_Summary; }) => { - const recentBackupsChart: { - idx: number; - time: number; - durationMs: number; - color: string; - bytesAdded: number; - }[] = []; const recentBackups = summary.recentBackups!; - for (let i = 0; i < recentBackups.timestampMs.length; i++) { - const color = colorForStatus(recentBackups.status[i]); - recentBackupsChart.push({ - idx: i, - time: Number(recentBackups.timestampMs[i]), - durationMs: Number(recentBackups.durationMs[i]), - color: color, - bytesAdded: Number(recentBackups.bytesAdded[i]), - }); - } - while (recentBackupsChart.length < 60) { - recentBackupsChart.push({ - idx: recentBackupsChart.length, - time: 0, - durationMs: 0, - color: "transparent", // transparent instead of white for dark mode support - bytesAdded: 0, - }); - } + const recentBackupsChart = useMemo(() => { + const chart: { + idx: number; + time: number; + durationMs: number; + color: string; + bytesAdded: number; + }[] = []; + for (let i = 0; i < recentBackups.timestampMs.length; i++) { + const color = colorForStatus(recentBackups.status[i]); + chart.push({ + idx: i, + time: Number(recentBackups.timestampMs[i]), + durationMs: Number(recentBackups.durationMs[i]), + color: color, + bytesAdded: Number(recentBackups.bytesAdded[i]), + }); + } + while (chart.length < 60) { + chart.push({ + idx: chart.length, + time: 0, + durationMs: 0, + color: "transparent", // transparent instead of white for dark mode support + bytesAdded: 0, + }); + } + return chart; + }, [recentBackups]); const BackupChartTooltip = ({ active, payload, label }: any) => { const idx = Number(label);