From e29c46cc5a9fc105a2923ff409d4989f5b9ae004 Mon Sep 17 00:00:00 2001 From: Thomas Camlong Date: Mon, 15 Jun 2026 15:39:01 +0200 Subject: [PATCH] fix(beszel): address final review findings - Gate stats/live queries on systems loading state to prevent stale ID requests during initial load - Simplify useLiveStats: functional setState, stable integrationKey dep, remove unused includeDocker param - Extract prepareRecords helper to deduplicate chart ordering logic - Remove stale timePeriod type cast --- .../src/beszel-system-stats/component.tsx | 13 +++++----- packages/widgets/src/beszel/_shared/chart.tsx | 13 ++++++---- .../src/beszel/_shared/use-live-stats.ts | 24 +++++++++---------- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/packages/widgets/src/beszel-system-stats/component.tsx b/packages/widgets/src/beszel-system-stats/component.tsx index 67adf9302..7e457182e 100644 --- a/packages/widgets/src/beszel-system-stats/component.tsx +++ b/packages/widgets/src/beszel-system-stats/component.tsx @@ -44,7 +44,7 @@ export default function BeszelSystemStatsWidget({ setOptions, }: WidgetComponentProps<"beszelSystemStats">) { const t = useScopedI18n("widget.beszelSystemStats"); - const { data: systemsResult = [] } = clientApi.widget.beszel.getSystems.useQuery( + const { data: systemsResult = [], isPending: systemsPending } = clientApi.widget.beszel.getSystems.useQuery( { integrationIds }, { staleTime: 30 * 1000 }, ); @@ -56,7 +56,8 @@ export default function BeszelSystemStatsWidget({ const selectedSystem = options.systemId || systems[0]?.value || ""; const selectedLabel = systems.find((s) => s.value === selectedSystem)?.label; - const systemExists = systems.length === 0 || systems.some((s) => s.value === selectedSystem); + const systemExists = systems.some((s) => s.value === selectedSystem); + const systemReady = !systemsPending && (selectedSystem === "" || systemExists); const handleSelectSystem = useCallback( (value: string) => setOptions({ newOptions: { systemId: value } }), @@ -70,13 +71,13 @@ export default function BeszelSystemStatsWidget({ { integrationIds, systemId: selectedSystem, - timePeriod: options.timePeriod as "1h" | "12h" | "24h" | "1w" | "30d", + timePeriod: options.timePeriod, includeDocker: showDocker, }, - { staleTime: 30 * 1000, enabled: !isLive && systemExists && selectedSystem !== "" }, + { staleTime: 30 * 1000, enabled: !isLive && systemReady && selectedSystem !== "" }, ); - const liveData = useLiveStats(integrationIds, selectedSystem, showDocker, isLive && systemExists); + const liveData = useLiveStats(integrationIds, selectedSystem, isLive && systemReady); const activeStats = isLive ? liveData : statsResult; @@ -122,7 +123,7 @@ export default function BeszelSystemStatsWidget({ const cols = width > 600 ? 2 : 1; - if (!systemExists && systems.length > 0) { + if (!systemsPending && !systemExists && systems.length > 0) { return (
diff --git a/packages/widgets/src/beszel/_shared/chart.tsx b/packages/widgets/src/beszel/_shared/chart.tsx index 08112d2f8..67b8957da 100644 --- a/packages/widgets/src/beszel/_shared/chart.tsx +++ b/packages/widgets/src/beszel/_shared/chart.tsx @@ -11,6 +11,13 @@ import type { BeszelContainerStatsRecord, BeszelSystemStatsRecord } from "@homar const formatTime = (timestamp: string) => dayjs(timestamp).format("HH:mm"); const formatTimeLive = (timestamp: string) => dayjs(timestamp).format("HH:mm:ss"); +function prepareRecords(records: T[], live: boolean) { + return { + fmt: live ? formatTimeLive : formatTime, + ordered: live ? records : [...records].toReversed(), + }; +} + const yAxisBase = { tickMargin: 0, tick: { fontSize: 10 } } as const; interface ChartPanelProps { @@ -82,8 +89,7 @@ export const useSystemChartData = ( ) => useMemo(() => { if (!systemStats) return []; - const fmt = live ? formatTimeLive : formatTime; - const ordered = live ? systemStats : [...systemStats].toReversed(); + const { fmt, ordered } = prepareRecords(systemStats, live); return ordered.map((r) => ({ time: fmt(r.created), ...mapFn(r.stats), @@ -123,8 +129,7 @@ export const useDockerChartData = ( if (!containerStats?.length) return []; const extract = defaultContainerExtractors[metric]; if (!extract) return []; - const fmt = live ? formatTimeLive : formatTime; - const ordered = live ? containerStats : [...containerStats].toReversed(); + const { fmt, ordered } = prepareRecords(containerStats, live); return ordered.map((record) => { const point: Record = { time: fmt(record.created) }; for (const name of containerNames) { diff --git a/packages/widgets/src/beszel/_shared/use-live-stats.ts b/packages/widgets/src/beszel/_shared/use-live-stats.ts index f664a9484..8338e4c49 100644 --- a/packages/widgets/src/beszel/_shared/use-live-stats.ts +++ b/packages/widgets/src/beszel/_shared/use-live-stats.ts @@ -1,6 +1,6 @@ "use client"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import { clientApi } from "@homarr/api/client"; import type { BeszelContainerStatsRecord, BeszelSystemStatsRecord } from "@homarr/integrations/types"; @@ -12,29 +12,29 @@ interface LiveStatsData { containerStats: BeszelContainerStatsRecord[]; } -export const useLiveStats = (integrationIds: string[], systemId: string, includeDocker: boolean, enabled: boolean) => { - const bufferRef = useRef({ systemStats: [], containerStats: [] }); +export const useLiveStats = (integrationIds: string[], systemId: string, enabled: boolean) => { const [data, setData] = useState(null); + const integrationKey = integrationIds.join(","); useEffect(() => { - bufferRef.current = { systemStats: [], containerStats: [] }; setData(null); - }, [systemId, enabled, ...integrationIds]); + }, [systemId, enabled, integrationKey]); const onData = useCallback( (incoming: { stats: BeszelSystemStatsRecord; containerStats: BeszelContainerStatsRecord | null }) => { - const buf = bufferRef.current; - buf.systemStats = [...buf.systemStats, incoming.stats].slice(-MAX_BUFFER); - if (incoming.containerStats) { - buf.containerStats = [...buf.containerStats, incoming.containerStats].slice(-MAX_BUFFER); - } - setData({ ...buf }); + setData((prev) => { + const systemStats = [...(prev?.systemStats ?? []), incoming.stats].slice(-MAX_BUFFER); + const containerStats = incoming.containerStats + ? [...(prev?.containerStats ?? []), incoming.containerStats].slice(-MAX_BUFFER) + : prev?.containerStats ?? []; + return { systemStats, containerStats }; + }); }, [], ); clientApi.widget.beszel.subscribeSystemStats.useSubscription( - { integrationIds, systemId, includeDocker }, + { integrationIds, systemId }, { enabled: enabled && systemId !== "", onData }, );