From aaf2c01ce1c20960ca4946d044f4600f4e1e8657 Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Thu, 10 Dec 2020 16:58:30 +0800 Subject: [PATCH] Fix custom generic store without a unsubscribe function (#27634) --- .../data/src/components/use-select/index.js | 3 +- .../src/components/use-select/test/index.js | 66 +++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/packages/data/src/components/use-select/index.js b/packages/data/src/components/use-select/index.js index f1443e842e4bf4..741ee366742e5b 100644 --- a/packages/data/src/components/use-select/index.js +++ b/packages/data/src/components/use-select/index.js @@ -199,7 +199,8 @@ export default function useSelect( _mapSelect, deps ) { return () => { isMountedAndNotUnsubscribing.current = false; - unsubscribers.forEach( ( unsubscribe ) => unsubscribe() ); + // The return value of the subscribe function could be undefined if the store is a custom generic store. + unsubscribers.forEach( ( unsubscribe ) => unsubscribe?.() ); renderQueue.flush( queueContext ); }; }, [ registry, trapSelect, depsChangedFlag ] ); diff --git a/packages/data/src/components/use-select/test/index.js b/packages/data/src/components/use-select/test/index.js index 4b8d8a9ce35c51..4dbfe576a2f5ef 100644 --- a/packages/data/src/components/use-select/test/index.js +++ b/packages/data/src/components/use-select/test/index.js @@ -719,5 +719,71 @@ describe( 'useSelect', () => { // Test if the unsubscribers get called correctly. renderer.unmount(); } ); + + it( 'handles custom generic stores without a unsubscribe function', () => { + let renderer; + + function createCustomStore() { + let storeChanged = () => {}; + let counter = 0; + + const selectors = { + getCounter: () => counter, + }; + + const actions = { + increment: () => { + counter += 1; + storeChanged(); + }, + }; + + return { + getSelectors() { + return selectors; + }, + getActions() { + return actions; + }, + subscribe( listener ) { + storeChanged = listener; + }, + }; + } + + registry.registerGenericStore( + 'generic-store', + createCustomStore() + ); + + const TestComponent = jest.fn( () => { + const state = useSelect( + ( select ) => select( 'generic-store' ).getCounter(), + [] + ); + + return
; + } ); + + act( () => { + renderer = TestRenderer.create( + + + + ); + } ); + + const testInstance = renderer.root; + + expect( testInstance.findByType( 'div' ).props.data ).toBe( 0 ); + + act( () => { + registry.dispatch( 'generic-store' ).increment(); + } ); + + expect( testInstance.findByType( 'div' ).props.data ).toBe( 1 ); + + expect( () => renderer.unmount() ).not.toThrow(); + } ); } ); } );