From a2e591cd361c1e98eb228d9909af411f77a86480 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Thu, 28 Apr 2022 13:02:55 +0200 Subject: [PATCH] useSelect: improve transition from async to sync mode --- .../data/src/components/use-select/index.js | 22 +++++++------------ .../src/components/use-select/test/index.js | 6 ++--- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/packages/data/src/components/use-select/index.js b/packages/data/src/components/use-select/index.js index a8d4d8ae4d2583..78fd9fc6b3c74c 100644 --- a/packages/data/src/components/use-select/index.js +++ b/packages/data/src/components/use-select/index.js @@ -112,11 +112,6 @@ export default function useSelect( mapSelect, deps ) { const registry = useRegistry(); const isAsync = useAsyncMode(); - // React can sometimes clear the `useMemo` cache. - // We use the cache-stable `useMemoOne` to avoid - // losing queues. - const queueContext = useMemoOne( () => ( { queue: true } ), [ registry ] ); - const [ , forceRender ] = useReducer( ( s ) => s + 1, 0 ); const latestRegistry = useRef( registry ); const latestMapSelect = useRef(); @@ -147,11 +142,13 @@ export default function useSelect( mapSelect, deps ) { mapOutput = latestMapOutput.current; const hasReplacedRegistry = latestRegistry.current !== registry; const hasReplacedMapSelect = latestMapSelect.current !== _mapSelect; + const hasLeftAsyncMode = latestIsAsync.current && ! isAsync; const lastMapSelectFailed = !! latestMapOutputError.current; if ( hasReplacedRegistry || hasReplacedMapSelect || + hasLeftAsyncMode || lastMapSelectFailed ) { try { @@ -178,19 +175,16 @@ export default function useSelect( mapSelect, deps ) { latestRegistry.current = registry; latestMapSelect.current = _mapSelect; + latestIsAsync.current = isAsync; latestMapOutput.current = mapOutput; latestMapOutputError.current = undefined; - - // This has to run after the other ref updates - // to avoid using stale values in the flushed - // callbacks or potentially overwriting a - // changed `latestMapOutput.current`. - if ( latestIsAsync.current !== isAsync ) { - latestIsAsync.current = isAsync; - renderQueue.flush( queueContext ); - } } ); + // React can sometimes clear the `useMemo` cache. + // We use the cache-stable `useMemoOne` to avoid + // losing queues. + const queueContext = useMemoOne( () => ( { queue: true } ), [ registry ] ); + const [ , forceRender ] = useReducer( ( s ) => s + 1, 0 ); const isMounted = useRef( false ); useIsomorphicLayoutEffect( () => { diff --git a/packages/data/src/components/use-select/test/index.js b/packages/data/src/components/use-select/test/index.js index d44ef997ff9221..4df4e4cb88fad7 100644 --- a/packages/data/src/components/use-select/test/index.js +++ b/packages/data/src/components/use-select/test/index.js @@ -884,10 +884,10 @@ describe( 'useSelect', () => { // Ensure the async update was flushed during the rerender. expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 1 ); - // initial render + subscription check + flushed store update + // initial render + subscription check + rerender with isAsync=false expect( selectSpy ).toHaveBeenCalledTimes( 3 ); - // initial render + rerender with isAsync=false + store state update - expect( TestComponent ).toHaveBeenCalledTimes( 3 ); + // initial render + rerender with isAsync=false + expect( TestComponent ).toHaveBeenCalledTimes( 2 ); } ); it( 'cancels scheduled updates when mapSelect function changes', async () => {