Commit f5abdd39be for woocommerce
commit f5abdd39bef881b1481d8c613a7744fe194f2ed0
Author: Christopher Allford <6451942+ObliviousHarmony@users.noreply.github.com>
Date: Wed Nov 26 09:15:18 2025 -0800
Auto-Paginate Variation Loading in `ProductControl` Component (#62114)
Rather than using an unbounded query, we will now
fetch all of the variations in pages.
diff --git a/plugins/woocommerce/changelog/62114-tweak-61755-paginate-all-variations b/plugins/woocommerce/changelog/62114-tweak-61755-paginate-all-variations
new file mode 100644
index 0000000000..4bd3c0a993
--- /dev/null
+++ b/plugins/woocommerce/changelog/62114-tweak-61755-paginate-all-variations
@@ -0,0 +1,4 @@
+Significance: minor
+Type: tweak
+
+Auto-paginate variation loading in the `ProductControl` component.
\ No newline at end of file
diff --git a/plugins/woocommerce/client/blocks/assets/js/editor-components/utils/index.js b/plugins/woocommerce/client/blocks/assets/js/editor-components/utils/index.js
index ec4d04a4fd..6a8f79a14a 100644
--- a/plugins/woocommerce/client/blocks/assets/js/editor-components/utils/index.js
+++ b/plugins/woocommerce/client/blocks/assets/js/editor-components/utils/index.js
@@ -53,18 +53,6 @@ const getProductsRequests = ( {
return requests;
};
-const uniqBy = ( array, iteratee ) => {
- const seen = new Map();
- return array.filter( ( item ) => {
- const key = iteratee( item );
- if ( ! seen.has( key ) ) {
- seen.set( key, item );
- return true;
- }
- return false;
- } );
-};
-
/**
* Get a promise that resolves to a list of products from the Store API.
*
@@ -82,19 +70,18 @@ export const getProducts = ( {
} ) => {
const requests = getProductsRequests( { selected, search, queryArgs } );
- return Promise.all( requests.map( ( path ) => apiFetch( { path } ) ) )
- .then( ( data ) => {
- const flatData = data.flat();
- const products = uniqBy( flatData, ( item ) => item.id );
- const list = products.map( ( product ) => ( {
- ...product,
- parent: 0,
- } ) );
- return list;
- } )
- .catch( ( e ) => {
- throw e;
- } );
+ return Promise.all( requests.map( ( path ) => apiFetch( { path } ) ) ).then(
+ ( data ) => [
+ ...new Map(
+ data.flatMap( ( products ) =>
+ products.map( ( item ) => [
+ item.id,
+ { ...item, parent: 0 },
+ ] )
+ )
+ ).values(),
+ ]
+ );
};
/**
@@ -169,10 +156,13 @@ export const getProductTags = ( { selected = [], search } ) => {
const requests = getProductTagsRequests( { selected, search } );
return Promise.all( requests.map( ( path ) => apiFetch( { path } ) ) ).then(
- ( data ) => {
- const flatData = data.flat();
- return uniqBy( flatData, ( item ) => item.id );
- }
+ ( data ) => [
+ ...new Map(
+ data.flatMap( ( tags ) =>
+ tags.map( ( item ) => [ item.id, item ] )
+ )
+ ).values(),
+ ]
);
};
@@ -207,13 +197,50 @@ export const getCategory = ( categoryId ) => {
* @param {number} product Product ID.
*/
export const getProductVariations = ( product ) => {
+ // Fetch the first page to get total page count from headers
return apiFetch( {
path: addQueryArgs( `wc/store/v1/products`, {
- per_page: 0,
+ per_page: 100,
type: 'variation',
parent: product,
+ page: 1,
} ),
- } );
+ parse: false,
+ } )
+ .then( ( response ) => {
+ const totalPages = parseInt(
+ response.headers.get( 'X-WP-TotalPages' ) || '1',
+ 10
+ );
+
+ // Parse the first page data
+ const firstPagePromise = response.json();
+
+ // Build array of fetch promises for remaining pages (starting at page 2)
+ const remainingRequests = [];
+ for ( let page = 2; page <= totalPages; page++ ) {
+ remainingRequests.push(
+ apiFetch( {
+ path: addQueryArgs( `wc/store/v1/products`, {
+ per_page: 100,
+ type: 'variation',
+ parent: product,
+ page,
+ } ),
+ } )
+ );
+ }
+
+ // Combine first page with remaining pages
+ return Promise.all( [ firstPagePromise, ...remainingRequests ] );
+ } )
+ .then( ( data ) => [
+ ...new Map(
+ data.flatMap( ( variations ) =>
+ variations.map( ( item ) => [ item.id, item ] )
+ )
+ ).values(),
+ ] );
};
/**