From 564f2ece0d071808cd9b53eeb0459bfdaeb8fe50 Mon Sep 17 00:00:00 2001 From: weimaozhen Date: Sat, 13 Jun 2026 10:23:56 +0700 Subject: [PATCH] fix(usage-stats): avoid partial stats on initial SSE race Skip creating partial stats from SSE before the initial REST load completes, keeping real-time merges limited to existing full stats. Co-authored-by: Cursor --- src/shared/components/UsageStats.js | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/shared/components/UsageStats.js b/src/shared/components/UsageStats.js index 69a55592..8585cd74 100644 --- a/src/shared/components/UsageStats.js +++ b/src/shared/components/UsageStats.js @@ -204,6 +204,7 @@ export default function UsageStats({ period: periodProp, setPeriod: setPeriodPro const [providers, setProviders] = useState([]); const [periodLocal, setPeriodLocal] = useState("today"); const isInitialLoad = useRef(true); + const hasLoadedStats = useRef(false); const period = periodProp ?? periodLocal; const setPeriod = setPeriodProp ?? setPeriodLocal; @@ -242,14 +243,17 @@ export default function UsageStats({ period: periodProp, setPeriod: setPeriodPro fetch(`/api/usage/stats?period=${period}`) .then((r) => r.ok ? r.json() : null) .then((data) => { - if (data) setStats((prev) => ({ ...prev, ...data })); + if (data) { + hasLoadedStats.current = true; + setStats((prev) => ({ ...prev, ...data })); + } }) .catch(() => {}) .finally(() => { setLoading(false); setFetching(false); }); - }, [period]); // eslint-disable-line react-hooks/exhaustive-deps + }, [period]); // SSE connection - real-time updates for activeRequests + recentRequests only useEffect(() => { @@ -259,14 +263,17 @@ export default function UsageStats({ period: periodProp, setPeriod: setPeriodPro try { const data = JSON.parse(e.data); // Always merge only real-time fields, never overwrite full stats from REST - setStats((prev) => ({ - ...(prev || {}), - activeRequests: data.activeRequests, - recentRequests: data.recentRequests, - errorProvider: data.errorProvider, - pending: data.pending, - })); - setLoading(false); + setStats((prev) => { + if (!prev) return prev; + return { + ...prev, + activeRequests: data.activeRequests, + recentRequests: data.recentRequests, + errorProvider: data.errorProvider, + pending: data.pending, + }; + }); + if (hasLoadedStats.current) setLoading(false); } catch (err) { console.error("[SSE CLIENT] parse error:", err); }