diff --git a/packages/data/src/components/use-select/index.js b/packages/data/src/components/use-select/index.js index d71f72b21c917..a87d20422a9ca 100644 --- a/packages/data/src/components/use-select/index.js +++ b/packages/data/src/components/use-select/index.js @@ -10,7 +10,7 @@ import { useEffect, useReducer, } from '@wordpress/element'; -import { isShallowEqualObjects } from '@wordpress/is-shallow-equal'; +import isShallowEqual from '@wordpress/is-shallow-equal'; /** * Internal dependencies @@ -90,16 +90,15 @@ export default function useSelect( _mapSelect, deps ) { let mapOutput; try { - if ( - latestMapSelect.current !== mapSelect || - latestMapOutputError.current - ) { + if ( latestMapSelect.current !== mapSelect || latestMapOutputError.current ) { mapOutput = mapSelect( registry.select, registry ); } else { mapOutput = latestMapOutput.current; } } catch ( error ) { - let errorMessage = `An error occurred while running 'mapSelect': ${ error.message }`; + let errorMessage = `An error occurred while running 'mapSelect': ${ + error.message + }`; if ( latestMapOutputError.current ) { errorMessage += `\nThe error may be correlated with this previous error:\n`; @@ -129,7 +128,7 @@ export default function useSelect( _mapSelect, deps ) { registry.select, registry ); - if ( isShallowEqualObjects( latestMapOutput.current, newMapOutput ) ) { + if ( isShallowEqual( latestMapOutput.current, newMapOutput ) ) { return; } latestMapOutput.current = newMapOutput; diff --git a/packages/data/src/components/use-select/test/index.js b/packages/data/src/components/use-select/test/index.js index ce8d15f3aa070..1aa2ad0d7b9d7 100644 --- a/packages/data/src/components/use-select/test/index.js +++ b/packages/data/src/components/use-select/test/index.js @@ -132,5 +132,91 @@ describe( 'useSelect', () => { children: 'bar', } ); } ); + describe( 'rerenders as expected with various mapSelect return types', () => { + const getComponent = ( mapSelectSpy ) => () => { + const data = useSelect( mapSelectSpy, [] ); + return
; + }; + let subscribedSpy, TestComponent; + const mapSelectSpy = jest.fn( + ( select ) => select( 'testStore' ).testSelector() + ); + const selectorSpy = jest.fn(); + const subscribeCallback = ( subscription ) => { + subscribedSpy = subscription; + }; + + beforeEach( () => { + registry.registerStore( 'testStore', { + reducer: () => null, + selectors: { + testSelector: selectorSpy, + }, + } ); + registry.subscribe = subscribeCallback; + TestComponent = getComponent( mapSelectSpy ); + } ); + afterEach( () => { + selectorSpy.mockClear(); + mapSelectSpy.mockClear(); + } ); + + it.each( [ + [ + 'boolean', + [ false, true ], + ], + [ + 'number', + [ 10, 20 ], + ], + [ + 'string', + [ 'bar', 'cheese' ], + ], + [ + 'array', + [ [ 10, 20 ], [ 10, 30 ] ], + ], + [ + 'object', + [ { foo: 'bar' }, { foo: 'cheese' } ], + ], + [ + 'null', + [ null, undefined ], + ], + [ + 'undefined', + [ undefined, 42 ], + ], + ] )( 'renders as expected with %s return values', ( + type, + testValues, + ) => { + const [ valueA, valueB ] = testValues; + selectorSpy.mockReturnValue( valueA ); + let renderer; + act( () => { + renderer = TestRenderer.create( + + + + ); + } ); + const testInstance = renderer.root; + // ensure expected state was rendered. + expect( testInstance.findByType( 'div' ).props.data ).toEqual( valueA ); + + // Update the returned value from the selector and trigger the + // subscription which should in turn trigger a re-render. + act( () => { + selectorSpy.mockReturnValue( valueB ); + subscribedSpy(); + } ); + expect( testInstance.findByType( 'div' ).props.data ).toEqual( valueB ); + expect( mapSelectSpy ).toHaveBeenCalledTimes( 3 ); + } ); + } ); } );