Enhance useCollection for conditionally returning previous results (https://github.com/woocommerce/woocommerce-blocks/pull/1182)

* add new config to `useCollection` options for controlling whether to invoke select or not.

also adds tests.

* update inline docs
This commit is contained in:
Darren Ethier 2019-11-15 11:11:48 -05:00 committed by GitHub
parent 47e082ef58
commit b4df4e056c
2 changed files with 85 additions and 7 deletions

View File

@ -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 ],
] );
} );
} );

View File

@ -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;
};