From ea9263de295c360d5d042349edf853b8aec3fc52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hartmut=20N=C3=B6renberg?= Date: Mon, 23 Mar 2026 09:36:37 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20ChargeabilityWidget=20hooks=20order=20?= =?UTF-8?q?=E2=80=94=20move=20useMemo=20before=20early=20return?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit React Rules of Hooks violation: filteredTop and filteredWatch useMemo hooks were placed AFTER the isLoading early return, causing "Rendered more hooks than during the previous render" error that crashed the entire dashboard. Fix: moved rawTop/rawWatch/month declarations and filteredTop/filteredWatch useMemo hooks BEFORE the isLoading early return so hooks always run in the same order. Verified in Chrome: dashboard loads with zero console errors. Co-Authored-By: claude-flow --- .../dashboard/widgets/ChargeabilityWidget.tsx | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/apps/web/src/components/dashboard/widgets/ChargeabilityWidget.tsx b/apps/web/src/components/dashboard/widgets/ChargeabilityWidget.tsx index bfbd638..f1669fa 100644 --- a/apps/web/src/components/dashboard/widgets/ChargeabilityWidget.tsx +++ b/apps/web/src/components/dashboard/widgets/ChargeabilityWidget.tsx @@ -145,6 +145,23 @@ export function ChargeabilityWidget({ config: _config, onConfigChange }: WidgetP setWatchVisibleCount(batchSize); }, [batchSize, includeProposed, selectedCountryIds, showDeparted]); + // These useMemo hooks MUST be before any early return to respect Rules of Hooks + const rawTop = data?.top ?? []; + const rawWatch = data?.watchlist ?? []; + const month = (data?.month as string) ?? ""; + + const filteredTop = useMemo(() => { + const arr = rawTop as ChargeabilityRow[]; + if (!chapterFilter) return arr; + return arr.filter((r) => r.chapter === chapterFilter); + }, [rawTop, chapterFilter]); + + const filteredWatch = useMemo(() => { + const arr = rawWatch as ChargeabilityRow[]; + if (!chapterFilter) return arr; + return arr.filter((r) => r.chapter === chapterFilter); + }, [rawWatch, chapterFilter]); + if (isLoading) { return (
@@ -171,22 +188,6 @@ export function ChargeabilityWidget({ config: _config, onConfigChange }: WidgetP ); } - const rawTop = data?.top ?? []; - const rawWatch = data?.watchlist ?? []; - const month = (data?.month as string) ?? ""; - - const filteredTop = useMemo(() => { - const arr = rawTop as ChargeabilityRow[]; - if (!chapterFilter) return arr; - return arr.filter((r) => r.chapter === chapterFilter); - }, [rawTop, chapterFilter]); - - const filteredWatch = useMemo(() => { - const arr = rawWatch as ChargeabilityRow[]; - if (!chapterFilter) return arr; - return arr.filter((r) => r.chapter === chapterFilter); - }, [rawWatch, chapterFilter]); - const top = ([...filteredTop]).sort((a, b) => { const mult = topDir === "asc" ? 1 : -1; switch (topSort) {