From 27d4a7aeaceb8eaf7205ef9f871d9b0edc1efe26 Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Thu, 28 May 2026 13:58:54 -0300 Subject: [PATCH] fix(cli): suppress react-hooks/set-state-in-effect for load-on-mount pattern (plan 14) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pre-existing lint error from F8 commit 2d58519ca9 — the new react-hooks/set-state-in-effect rule conservatively flags any setState within useEffect body, even when setState happens async after Promise resolution. The "load remote data on mount" pattern is canonical until React 19 use()/Suspense migration. Fix adds cancelled-flag pattern to prevent setState after unmount + block-disable with justification comment. Tests still pass 5/5. Found during code review v2 deep audit — F10 audit reported "lint 0 errors" but only ran focused lint, not full project (which surfaces ~2985 pre-existing warnings + this 1 new error). --- .../cli-code/components/ToolDetailClient.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/app/(dashboard)/dashboard/cli-code/components/ToolDetailClient.tsx b/src/app/(dashboard)/dashboard/cli-code/components/ToolDetailClient.tsx index 77c7ada95b..c48c42b376 100644 --- a/src/app/(dashboard)/dashboard/cli-code/components/ToolDetailClient.tsx +++ b/src/app/(dashboard)/dashboard/cli-code/components/ToolDetailClient.tsx @@ -84,12 +84,25 @@ export default function ToolDetailClient({ toolId, category }: ToolDetailClientP }, []); useEffect(() => { + let cancelled = false; + // "Load data on mount" pattern: fetch* callbacks call setState internally + // after async resolution. The cancelled flag prevents setState after unmount. + // The react-hooks/set-state-in-effect rule flags this pattern conservatively + // (it cannot distinguish sync vs async setState), but this is the canonical + // way to load remote data on mount until we migrate to use()/Suspense. + /* eslint-disable react-hooks/set-state-in-effect */ Promise.all([ fetchConnections(), fetchApiKeys(), fetchCloudSettings(), fetchDynamicModels(), - ]).finally(() => setLoading(false)); + ]).finally(() => { + if (!cancelled) setLoading(false); + }); + /* eslint-enable react-hooks/set-state-in-effect */ + return () => { + cancelled = true; + }; }, [fetchConnections, fetchApiKeys, fetchCloudSettings, fetchDynamicModels]); const getActiveProviders = useCallback(() => {