From 0a828fbe4fcc033bde766f3dc0abcd0e797b674e Mon Sep 17 00:00:00 2001 From: abhishek Date: Wed, 30 Sep 2026 12:23:50 +0530 Subject: [PATCH] health score: move the non-food early return past the hook `if (!isEdible(category)) return null` sat on the line above `useQuery`, which made the hook conditional. React counts hooks per component instance, so one drawer reused for two products -- a soap and then a biscuit, which is ordinary browsing in the global catalogue -- went 0 hooks then 1 and threw "rendered more hooks than during the previous render". That does not degrade the panel, it unmounts the tree: the health score then disappears for EVERY product until the page is reloaded, which reads exactly like the feature having been switched off. The guard now sits after the hook, and `enabled` carries the intent the early return was protecting -- a non-food product still asks the service nothing. Co-Authored-By: Claude Opus 5 --- src/features/store-admin/HealthScorePanel.tsx | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/features/store-admin/HealthScorePanel.tsx b/src/features/store-admin/HealthScorePanel.tsx index e4d82bc..f05634a 100644 --- a/src/features/store-admin/HealthScorePanel.tsx +++ b/src/features/store-admin/HealthScorePanel.tsx @@ -56,14 +56,25 @@ export function HealthScorePanel({ populated for both, so nothing in the response separates them. Our own category does, so the decision is made here until they publish theirs. - Before the network call, deliberately: there is nothing to ask about. + Still no network call for one — see `enabled` below — but the EARLY RETURN + that used to sit here has moved past the hook. + + It read `if (!isEdible(category)) return null` on the line above `useQuery`, + which made the hook conditional. React counts hooks per component instance, so + one drawer reused for two products — a soap and then a biscuit, which is + ordinary browsing in the global catalogue — went 0 hooks then 1, and React + threw "rendered more hooks than during the previous render". That does not + degrade the panel, it unmounts the tree: the health score then disappears for + EVERY product until the page is reloaded, which reads exactly like the feature + being switched off. */ - if (!isEdible(category)) return null; + const isFood = isEdible(category); const query = useQuery({ queryKey: ['nutrition', brand, imageId], queryFn: () => nutritionApi.forProduct(brand, imageId), - enabled: Boolean(brand && imageId), + // Non-food asks nothing, which is what the early return was protecting. + enabled: Boolean(brand && imageId && isFood), // Nutrition for a packaged product does not change during a trading day. staleTime: 60 * 60_000, refetchOnWindowFocus: false, @@ -72,6 +83,9 @@ export function HealthScorePanel({ retry: false, }); + // Past the hook, so the count is the same on every render. + if (!isFood) return null; + /* * No catalogue link, so there is nothing to look up. *