From 52298d826a77265f31b73303d1b5e2b219b6d2ce Mon Sep 17 00:00:00 2001 From: Wooseong Kim Date: Fri, 7 Aug 2026 08:42:05 +0900 Subject: [PATCH] fix(github-project): own slug-index retry timer cleanup Move the failure-retry setTimeout into its own effect so cleanup always clears it. Scheduling from the async buildIndex then-handler failed the react-doctor effect-needs-cleanup gate in static analysis. --- src/renderer/src/lib/repo-slug-index.ts | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/renderer/src/lib/repo-slug-index.ts b/src/renderer/src/lib/repo-slug-index.ts index 457ea8651e6..d48dbb869f3 100644 --- a/src/renderer/src/lib/repo-slug-index.ts +++ b/src/renderer/src/lib/repo-slug-index.ts @@ -187,35 +187,44 @@ export function useRepoSlugIndex(): RepoSlugIndexState { const [upstreamIndex, setUpstreamIndex] = useState(() => new Map()) const [ready, setReady] = useState(false) const [retryGeneration, setRetryGeneration] = useState(0) + // Why: schedule retry in a dedicated effect so setTimeout cleanup is owned + // synchronously (react-doctor effect-needs-cleanup); async .then assignment + // was not statically owned by the buildIndex effect cleanup. + const [retryDelayMs, setRetryDelayMs] = useState(null) // Why: track the current repos snapshot so the effect can ignore stale // resolutions when repos change mid-flight. const generationRef = useRef(0) useEffect(() => { const gen = ++generationRef.current - let retryTimer: ReturnType | undefined setReady(false) + setRetryDelayMs(null) void buildIndex(repos, settings).then( - ({ index: next, upstreamIndex: nextUpstream, retryDelayMs }) => { + ({ index: next, upstreamIndex: nextUpstream, retryDelayMs: nextRetryDelayMs }) => { if (gen !== generationRef.current) { return } setIndex(next) setUpstreamIndex(nextUpstream) setReady(true) - if (retryDelayMs !== null) { - retryTimer = setTimeout(() => setRetryGeneration((value) => value + 1), retryDelayMs) - } + setRetryDelayMs(nextRetryDelayMs) } ) return () => { generationRef.current += 1 - if (retryTimer) { - clearTimeout(retryTimer) - } } }, [repos, retryGeneration, settings]) + useEffect(() => { + if (retryDelayMs === null) { + return + } + const retryTimer = setTimeout(() => setRetryGeneration((value) => value + 1), retryDelayMs) + return () => { + clearTimeout(retryTimer) + } + }, [retryDelayMs]) + return useMemo(() => { const lookupSlugMatches = (slug: string | null | undefined, host?: string): RepoSlugMatches => { const [owner, repo] = slug?.split('/') ?? []