diff --git a/plugins/woocommerce-blocks/assets/js/base/hooks/test/use-collection.js b/plugins/woocommerce-blocks/assets/js/base/hooks/test/use-collection.js index 1d4f0394926..b21225374dd 100644 --- a/plugins/woocommerce-blocks/assets/js/base/hooks/test/use-collection.js +++ b/plugins/woocommerce-blocks/assets/js/base/hooks/test/use-collection.js @@ -35,7 +35,7 @@ class TestErrorBoundary extends ReactComponent { } } -describe( 'useStoreProducts', () => { +describe( 'useCollection', () => { let registry, mocks, renderer; const getProps = ( testRenderer ) => { const { results, isLoading } = testRenderer.root.findByType( @@ -233,4 +233,65 @@ describe( 'useStoreProducts', () => { renderer.unmount(); } ); + it( 'should return previous query results if `shouldSelect` is false', () => { + mocks.selectors.getCollection.mockImplementation( + ( state, ...args ) => { + return args; + } + ); + const TestComponent = getTestComponent(); + act( () => { + renderer = TestRenderer.create( + getWrappedComponents( TestComponent, { + options: { + namespace: 'test/store', + resourceName: 'products', + resourceValues: [ 10, 20 ], + }, + } ) + ); + } ); + const { results } = getProps( renderer ); + // rerender but with shouldSelect to false + act( () => { + renderer.update( + getWrappedComponents( TestComponent, { + options: { + namespace: 'test/store', + resourceName: 'productsb', + resourceValues: [ 10, 30 ], + shouldSelect: false, + }, + } ) + ); + } ); + const { results: results2 } = getProps( renderer ); + expect( results2 ).toBe( results ); + // expect 2 calls because internally, useSelect invokes callback twice + // on mount. + expect( mocks.selectors.getCollection ).toHaveBeenCalledTimes( 2 ); + + // rerender again but set shouldSelect to true again and we should see + // new results + act( () => { + renderer.update( + getWrappedComponents( TestComponent, { + options: { + namespace: 'test/store', + resourceName: 'productsb', + resourceValues: [ 10, 30 ], + shouldSelect: true, + }, + } ) + ); + } ); + const { results: results3 } = getProps( renderer ); + expect( results3 ).not.toEqual( results ); + expect( results3 ).toEqual( [ + 'test/store', + 'productsb', + {}, + [ 10, 30 ], + ] ); + } ); } ); diff --git a/plugins/woocommerce-blocks/assets/js/base/hooks/use-collection.js b/plugins/woocommerce-blocks/assets/js/base/hooks/use-collection.js index 1609ab931db..a82c20c07ed 100644 --- a/plugins/woocommerce-blocks/assets/js/base/hooks/use-collection.js +++ b/plugins/woocommerce-blocks/assets/js/base/hooks/use-collection.js @@ -3,6 +3,7 @@ */ import { COLLECTIONS_STORE_KEY as storeKey } from '@woocommerce/block-data'; import { useSelect } from '@wordpress/data'; +import { useRef } from '@wordpress/element'; /** * Internal dependencies @@ -29,6 +30,9 @@ import { useShallowEqual } from './use-shallow-equal'; * query to execute on the collection * (optional). Example: * `{ order: 'ASC', order_by: 'price' }` + * @param {boolean} options.shouldSelect If false, the previous results will be + * returned and internal selects will not + * fire. * * @return {Object} This hook will return an object with two properties: * - results An array of collection items returned. @@ -41,6 +45,7 @@ export const useCollection = ( options ) => { resourceName, resourceValues = [], query = {}, + shouldSelect = true, } = options; if ( ! namespace || ! resourceName ) { throw new Error( @@ -48,11 +53,15 @@ export const useCollection = ( options ) => { 'the resource properties.' ); } + const currentResults = useRef( { results: [], isLoading: true } ); // ensure we feed the previous reference if it's equivalent const currentQuery = useShallowEqual( query ); const currentResourceValues = useShallowEqual( resourceValues ); - const { results = [], isLoading = true } = useSelect( + const results = useSelect( ( select ) => { + if ( ! shouldSelect ) { + return null; + } const store = select( storeKey ); // filter out query if it is undefined. const args = [ @@ -69,10 +78,18 @@ export const useCollection = ( options ) => { ), }; }, - [ namespace, resourceName, currentResourceValues, currentQuery ] + [ + namespace, + resourceName, + currentResourceValues, + currentQuery, + shouldSelect, + ] ); - return { - results, - isLoading, - }; + // if selector was not bailed, then update current results. Otherwise return + // previous results + if ( results !== null ) { + currentResults.current = results; + } + return currentResults.current; };