From 0872c5f6ec1da200e4de680bb8b2405f4cfd03e2 Mon Sep 17 00:00:00 2001 From: Neil Date: Thu, 10 Sep 2026 22:04:31 -0700 Subject: [PATCH] fix(diff): keep the load registry live across a StrictMode effect replay StrictMode runs setup -> cleanup -> setup with no render between the two setups, so the render-time write alone left the flag false for the life of the replayed mount and every later reload silently bailed. Write it in both places: render covers the window where child effects run before this parent's, the effect covers the replayed cleanup. --- .../combined-diff-section-load-registry.ts | 6 +++-- .../use-combined-diff-section-retry.test.tsx | 24 ++++++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/renderer/src/components/editor/combined-diff/load-sections/combined-diff-section-load-registry.ts b/src/renderer/src/components/editor/combined-diff/load-sections/combined-diff-section-load-registry.ts index 3743a490125..bbb8cd28833 100644 --- a/src/renderer/src/components/editor/combined-diff/load-sections/combined-diff-section-load-registry.ts +++ b/src/renderer/src/components/editor/combined-diff/load-sections/combined-diff-section-load-registry.ts @@ -59,8 +59,9 @@ export function useCombinedDiffSectionLoadRegistry( loadSchedulerRef.current ??= createCombinedDiffLoadScheduler({ loadSection: (index) => loadSectionRef.current(index) }) - // Why here and not in the effect below: child effects run before this parent's, so StrictMode's - // replayed mount would leave a window where the ref reads false while the viewer is live. + // Why in render AND in the effect below: child effects run before this parent's, so a + // render-only write covers that window; StrictMode replays setup -> cleanup -> setup with no + // render in between, so an effect-only write is needed to survive the replayed cleanup. registryLiveRef.current = true useEffect(() => { @@ -68,6 +69,7 @@ export function useCombinedDiffSectionLoadRegistry( const scheduler = loadSchedulerRef.current const reloadTimers = reloadTimersRef.current scheduler.reset() + registryLiveRef.current = true return () => { registryLiveRef.current = false clearPendingSectionReloadTimers(reloadTimers) diff --git a/src/renderer/src/components/editor/combined-diff/load-sections/use-combined-diff-section-retry.test.tsx b/src/renderer/src/components/editor/combined-diff/load-sections/use-combined-diff-section-retry.test.tsx index 9ee757f3d6c..1d12b6549fc 100644 --- a/src/renderer/src/components/editor/combined-diff/load-sections/use-combined-diff-section-retry.test.tsx +++ b/src/renderer/src/components/editor/combined-diff/load-sections/use-combined-diff-section-retry.test.tsx @@ -1,7 +1,7 @@ // @vitest-environment happy-dom import { act, renderHook } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' -import { useRef } from 'react' +import { StrictMode, useRef } from 'react' import type { DiffSection } from '../../diff-section-types' import { useCombinedDiffSectionLoadRegistry } from './combined-diff-section-load-registry' import { useCombinedDiffSectionRetry } from './use-combined-diff-section-retry' @@ -77,3 +77,25 @@ describe('deferred section reloads', () => { expect(invalidate).not.toHaveBeenCalled() }) }) + +it('still reloads after StrictMode replays the registry effect', () => { + // Why: StrictMode runs setup -> cleanup -> setup with no render between the two setups, so a + // render-only live flag stays false for the life of the replayed mount. + const view = renderHook( + () => { + const sectionsRef = useRef([section()]) + const registry = useCombinedDiffSectionLoadRegistry(sectionsRef) + const actions = useCombinedDiffSectionRetry({ + invalidateViewStateCache: invalidate, + registry, + setSectionHeights: vi.fn(), + setSections: vi.fn() + }) + registry.renderedIndicesRef.current.add(0) + return actions + }, + { wrapper: StrictMode } + ) + act(() => view.result.current.requestSectionReload(0)) + expect(invalidate).toHaveBeenCalled() +})