From 6cfe7a78a82929eddd475f12a1b31cb50f3fb1db Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Mon, 7 Dec 2020 11:01:10 +0800 Subject: [PATCH] Fallback to regular subscribe if the store doesn't exist in useSelect (#27466) --- .../src/components/use-select/test/index.js | 154 ++++++++++++++++++ packages/data/src/registry.js | 8 + 2 files changed, 162 insertions(+) diff --git a/packages/data/src/components/use-select/test/index.js b/packages/data/src/components/use-select/test/index.js index d5415191ffcddf..4b8d8a9ce35c51 100644 --- a/packages/data/src/components/use-select/test/index.js +++ b/packages/data/src/components/use-select/test/index.js @@ -283,6 +283,9 @@ describe( 'useSelect', () => { expect( selectCount2 ).toHaveBeenCalledTimes( 3 ); expect( TestComponent ).toHaveBeenCalledTimes( 3 ); expect( testInstance.findByType( 'div' ).props.data ).toBe( 1 ); + + // Test if the unsubscribers get called correctly. + renderer.unmount(); } ); it( 'can subscribe to multiple stores at once', () => { @@ -565,5 +568,156 @@ describe( 'useSelect', () => { childCount: 0, } ); } ); + + it( 'handles non-existing stores', () => { + registry.registerStore( 'store-1', counterStore ); + + let renderer; + + const TestComponent = jest.fn( () => { + const state = useSelect( + ( select ) => ( { + count1: select( 'store-1' ).getCounter(), + blank: select( 'non-existing-store' )?.getCounter(), + } ), + [] + ); + + return
; + } ); + + act( () => { + renderer = TestRenderer.create( + + + + ); + } ); + + const testInstance = renderer.root; + + expect( testInstance.findByType( 'div' ).props.data ).toEqual( { + count1: 0, + blank: undefined, + } ); + + act( () => { + registry.dispatch( 'store-1' ).increment(); + } ); + + expect( testInstance.findByType( 'div' ).props.data ).toEqual( { + count1: 1, + blank: undefined, + } ); + + // Test if the unsubscribers get called correctly. + renderer.unmount(); + } ); + + it( 'handles registration of a non-existing store during rendering', () => { + let renderer; + + const TestComponent = jest.fn( () => { + const state = useSelect( + ( select ) => + select( 'not-yet-registered-store' )?.getCounter(), + [] + ); + + return
; + } ); + + act( () => { + renderer = TestRenderer.create( + + + + ); + } ); + + const testInstance = renderer.root; + + expect( testInstance.findByType( 'div' ).props.data ).toBe( + undefined + ); + + act( () => { + registry.registerStore( + 'not-yet-registered-store', + counterStore + ); + } ); + + // This is not ideal, but is the way it's working before and we want to prevent breaking changes. + expect( testInstance.findByType( 'div' ).props.data ).toBe( + undefined + ); + + act( () => { + registry.dispatch( 'not-yet-registered-store' ).increment(); + } ); + + expect( testInstance.findByType( 'div' ).props.data ).toBe( 1 ); + + // Test if the unsubscribers get called correctly. + renderer.unmount(); + } ); + + it( 'handles registration of a non-existing store of sub-registry during rendering', () => { + let renderer; + + const subRegistry = createRegistry( {}, registry ); + + const TestComponent = jest.fn( () => { + const state = useSelect( + ( select ) => + select( + 'not-yet-registered-child-store' + )?.getCounter(), + [] + ); + + return
; + } ); + + act( () => { + renderer = TestRenderer.create( + + + + + + ); + } ); + + const testInstance = renderer.root; + + expect( testInstance.findByType( 'div' ).props.data ).toBe( + undefined + ); + + act( () => { + registry.registerStore( + 'not-yet-registered-child-store', + counterStore + ); + } ); + + // This is not ideal, but is the way it's working before and we want to prevent breaking changes. + expect( testInstance.findByType( 'div' ).props.data ).toBe( + undefined + ); + + act( () => { + registry + .dispatch( 'not-yet-registered-child-store' ) + .increment(); + } ); + + expect( testInstance.findByType( 'div' ).props.data ).toBe( 1 ); + + // Test if the unsubscribers get called correctly. + renderer.unmount(); + } ); } ); } ); diff --git a/packages/data/src/registry.js b/packages/data/src/registry.js index cda0b555c54807..6a0ca9fc4a7a4d 100644 --- a/packages/data/src/registry.js +++ b/packages/data/src/registry.js @@ -234,6 +234,14 @@ export function createRegistry( storeConfigs = {}, parent = null ) { return stores[ storeName ].subscribe( handler ); } + // Trying to access a store that hasn't been registered, + // this is a pattern rarely used but seen in some places. + // We fallback to regular `subscribe` here for backward-compatibility for now. + // See https://github.com/WordPress/gutenberg/pull/27466 for more info. + if ( ! parent ) { + return subscribe( handler ); + } + return parent.__experimentalSubscribeStore( storeName, handler ); }