@wordpress/core-data
Version:
Access to and manipulation of core WordPress entities.
144 lines (124 loc) • 4.43 kB
JavaScript
import EquivalentKeyMap from 'equivalent-key-map';
import getQueryParts from './get-query-parts';
import { setNestedValue } from '../utils';
/**
* Cache of state keys to EquivalentKeyMap where the inner map tracks queries
* to their resulting items set. WeakMap allows garbage collection on expired
* state references.
*
* @type {WeakMap<Object,EquivalentKeyMap>}
*/
const queriedItemsCacheByState = new WeakMap();
/**
* Returns items for a given query, or null if the items are not known.
*
* @param {Object} state State object.
* @param {?Object} query Optional query.
* @param {?Object} options Optional pagination options.
* @param {boolean} options.supportsPagination Whether the entity supports pagination. Default true.
*
* @return {?Array} Query items.
*/
function getQueriedItemsUncached( state, query, options = {} ) {
const { supportsPagination = true } = options;
const {
stableKey,
page,
perPage,
offset: queryOffset,
include,
fields,
context,
} = getQueryParts( query );
const itemIds = state.queries?.[ context ]?.[ stableKey ]?.itemIds;
if ( ! itemIds ) {
return null;
}
const isPaginated = supportsPagination && perPage !== -1;
const startOffset = isPaginated ? queryOffset ?? ( page - 1 ) * perPage : 0;
const endOffset = isPaginated
? Math.min( startOffset + perPage, itemIds.length )
: itemIds.length;
// If the requested page range exceeds the stored itemIds, the data for
// this specific pagination window may not have been fetched yet. Return
// null unless totalItems confirms we already have all available items.
if ( isPaginated && itemIds.length < startOffset + perPage ) {
const totalItems =
state.queries[ context ][ stableKey ].meta?.totalItems;
if ( Number.isFinite( totalItems ) && itemIds.length < totalItems ) {
return null;
}
}
const items = [];
for ( let i = startOffset; i < endOffset; i++ ) {
const itemId = itemIds[ i ];
if ( Array.isArray( include ) && ! include.includes( itemId ) ) {
continue;
}
if ( itemId === undefined ) {
continue;
}
// Having a target item ID doesn't guarantee that this object has been queried.
if ( ! state.items[ context ]?.hasOwnProperty( itemId ) ) {
return null;
}
const item = state.items[ context ][ itemId ];
let filteredItem;
if ( Array.isArray( fields ) ) {
filteredItem = {};
for ( let f = 0; f < fields.length; f++ ) {
const field = fields[ f ].split( '.' );
let value = item;
field.forEach( ( fieldName ) => {
value = value?.[ fieldName ];
} );
setNestedValue( filteredItem, field, value );
}
} else {
// If expecting a complete item, validate that completeness, or
// otherwise abort.
if ( ! state.itemIsComplete[ context ]?.[ itemId ] ) {
return null;
}
filteredItem = item;
}
items.push( filteredItem );
}
return items;
}
/**
* Returns items for a given query, or null if the items are not known. Caches
* result per state (by reference) and per query (by deep equality), so that:
*
* `getQueriedItems( state, {} ) === getQueriedItems( state, {} )`
*
* @param {Object} state State object.
* @param {?Object} query Optional query.
* @param {?Object} options Optional pagination options.
* @param {boolean} options.supportsPagination Whether the entity supports pagination. Default true.
*
* @return {?Array} Query items.
*/
export function getQueriedItems( state, query = {}, options = {} ) {
let queriedItemsCache = queriedItemsCacheByState.get( state );
if ( queriedItemsCache ) {
const queriedItems = queriedItemsCache.get( query );
if ( queriedItems !== undefined ) {
return queriedItems;
}
} else {
queriedItemsCache = new EquivalentKeyMap();
queriedItemsCacheByState.set( state, queriedItemsCache );
}
const items = getQueriedItemsUncached( state, query, options );
queriedItemsCache.set( query, items );
return items;
}
export function getQueriedTotalItems( state, query = {} ) {
const { stableKey, context } = getQueryParts( query );
return state.queries?.[ context ]?.[ stableKey ]?.meta?.totalItems ?? null;
}
export function getQueriedTotalPages( state, query = {} ) {
const { stableKey, context } = getQueryParts( query );
return state.queries?.[ context ]?.[ stableKey ]?.meta?.totalPages ?? null;
}