From ab08f21f8faa77bbbd0ec18c9b75eed2e3c1fbb8 Mon Sep 17 00:00:00 2001 From: Sarah Norris Date: Fri, 22 Sep 2023 19:09:52 +0100 Subject: [PATCH] Memoize `useSelect` for `usePatterns` (#54588) * Memoize useSelect for usePatterns * Revert memoized selector in resolver # Conflicts: # packages/edit-site/src/components/page-patterns/use-patterns.js --- .../components/page-patterns/use-patterns.js | 313 +++++++++++------- 1 file changed, 192 insertions(+), 121 deletions(-) diff --git a/packages/edit-site/src/components/page-patterns/use-patterns.js b/packages/edit-site/src/components/page-patterns/use-patterns.js index daaa9dd7822da..b43b21c231861 100644 --- a/packages/edit-site/src/components/page-patterns/use-patterns.js +++ b/packages/edit-site/src/components/page-patterns/use-patterns.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import createSelector from 'rememo'; + /** * WordPress dependencies */ @@ -41,100 +46,55 @@ const templatePartToPattern = ( templatePart ) => ( { templatePart, } ); -const selectTemplatePartsAsPatterns = ( - select, - { categoryId, search = '' } = {} -) => { - const { getEntityRecords, getIsResolving } = select( coreStore ); - const { __experimentalGetDefaultTemplatePartAreas } = select( editorStore ); - const query = { per_page: -1 }; - const rawTemplateParts = - getEntityRecords( 'postType', TEMPLATE_PART_POST_TYPE, query ) ?? - EMPTY_PATTERN_LIST; - const templateParts = rawTemplateParts.map( ( templatePart ) => - templatePartToPattern( templatePart ) - ); - - // In the case where a custom template part area has been removed we need - // the current list of areas to cross check against so orphaned template - // parts can be treated as uncategorized. - const knownAreas = __experimentalGetDefaultTemplatePartAreas() || []; - const templatePartAreas = knownAreas.map( ( area ) => area.area ); - - const templatePartHasCategory = ( item, category ) => { - if ( category !== 'uncategorized' ) { - return item.templatePart.area === category; - } - - return ( - item.templatePart.area === category || - ! templatePartAreas.includes( item.templatePart.area ) +const selectTemplatePartsAsPatterns = createSelector( + ( select, categoryId, search = '' ) => { + const { getEntityRecords, getIsResolving } = select( coreStore ); + const { __experimentalGetDefaultTemplatePartAreas } = + select( editorStore ); + const query = { per_page: -1 }; + const rawTemplateParts = + getEntityRecords( 'postType', TEMPLATE_PART_POST_TYPE, query ) ?? + EMPTY_PATTERN_LIST; + const templateParts = rawTemplateParts.map( ( templatePart ) => + templatePartToPattern( templatePart ) ); - }; - - const isResolving = getIsResolving( 'getEntityRecords', [ - 'postType', - 'wp_template_part', - query, - ] ); - - const patterns = searchItems( templateParts, search, { - categoryId, - hasCategory: templatePartHasCategory, - } ); - - return { patterns, isResolving }; -}; -const selectThemePatterns = ( select ) => { - const { getSettings } = unlock( select( editSiteStore ) ); - const settings = getSettings(); - const blockPatterns = - settings.__experimentalAdditionalBlockPatterns ?? - settings.__experimentalBlockPatterns; + // In the case where a custom template part area has been removed we need + // the current list of areas to cross check against so orphaned template + // parts can be treated as uncategorized. + const knownAreas = __experimentalGetDefaultTemplatePartAreas() || []; + const templatePartAreas = knownAreas.map( ( area ) => area.area ); - const restBlockPatterns = select( coreStore ).getBlockPatterns(); - - const patterns = [ - ...( blockPatterns || [] ), - ...( restBlockPatterns || [] ), - ] - .filter( - ( pattern ) => ! PATTERN_CORE_SOURCES.includes( pattern.source ) - ) - .filter( filterOutDuplicatesByName ) - .filter( ( pattern ) => pattern.inserter !== false ) - .map( ( pattern ) => ( { - ...pattern, - keywords: pattern.keywords || [], - type: PATTERN_TYPES.theme, - blocks: parse( pattern.content, { - __unstableSkipMigrationLogs: true, - } ), - } ) ); - - return { patterns, isResolving: false }; -}; -const selectPatterns = ( - select, - { categoryId, search = '', syncStatus } = {} -) => { - const { patterns: themePatterns } = selectThemePatterns( select ); - const { patterns: userPatterns } = selectUserPatterns( select ); + const templatePartHasCategory = ( item, category ) => { + if ( category !== TEMPLATE_PART_AREA_DEFAULT_CATEGORY ) { + return item.templatePart.area === category; + } let patterns = [ ...( themePatterns || [] ), ...( userPatterns || [] ) ]; + return ( + item.templatePart.area === category || + ! templatePartAreas.includes( item.templatePart.area ) + ); + }; if ( syncStatus ) { patterns = patterns.filter( ( pattern ) => pattern.syncStatus === syncStatus ); } + const isResolving = getIsResolving( 'getEntityRecords', [ + 'postType', + TEMPLATE_PART_POST_TYPE, + query, + ] ); if ( categoryId ) { patterns = searchItems( patterns, search, { + const patterns = searchItems( templateParts, search, { categoryId, hasCategory: ( item, currentCategory ) => item.categories?.includes( currentCategory ), + hasCategory: templatePartHasCategory, } ); } else { patterns = searchItems( patterns, search, { @@ -144,6 +104,96 @@ const selectPatterns = ( return { patterns, isResolving: false }; }; + return { patterns, isResolving }; + }, + ( select ) => [ + select( coreStore ).getEntityRecords( + 'postType', + TEMPLATE_PART_POST_TYPE, + { + per_page: -1, + } + ), + select( coreStore ).getIsResolving( 'getEntityRecords', [ + 'postType', + TEMPLATE_PART_POST_TYPE, + { per_page: -1 }, + ] ), + select( editorStore ).__experimentalGetDefaultTemplatePartAreas(), + ] +); + +const selectThemePatterns = createSelector( + ( select ) => { + const { getSettings } = unlock( select( editSiteStore ) ); + const settings = getSettings(); + const blockPatterns = + settings.__experimentalAdditionalBlockPatterns ?? + settings.__experimentalBlockPatterns; + + const restBlockPatterns = select( coreStore ).getBlockPatterns(); + + const patterns = [ + ...( blockPatterns || [] ), + ...( restBlockPatterns || [] ), + ] + .filter( + ( pattern ) => ! PATTERN_CORE_SOURCES.includes( pattern.source ) + ) + .filter( filterOutDuplicatesByName ) + .filter( ( pattern ) => pattern.inserter !== false ) + .map( ( pattern ) => ( { + ...pattern, + keywords: pattern.keywords || [], + type: PATTERN_TYPES.theme, + blocks: parse( pattern.content, { + __unstableSkipMigrationLogs: true, + } ), + } ) ); + + return { patterns, isResolving: false }; + }, + ( select ) => [ + select( coreStore ).getBlockPatterns(), + unlock( select( editSiteStore ) ).getSettings(), + ] +); + +const selectPatterns = createSelector( + ( select, categoryId, syncStatus, search = '' ) => { + const { patterns: themePatterns } = selectThemePatterns( select ); + const { patterns: userPatterns } = selectUserPatterns( select ); + + let patterns = [ + ...( themePatterns || [] ), + ...( userPatterns || [] ), + ]; + + if ( syncStatus ) { + patterns = patterns.filter( + ( pattern ) => pattern.syncStatus === syncStatus + ); + } + + if ( categoryId ) { + patterns = searchItems( patterns, search, { + categoryId, + hasCategory: ( item, currentCategory ) => + item.categories?.includes( currentCategory ), + } ); + } else { + patterns = searchItems( patterns, search, { + hasCategory: ( item ) => ! item.hasOwnProperty( 'categories' ), + } ); + } + return { patterns, isResolving: false }; + }, + ( select ) => [ + selectThemePatterns( select ), + selectUserPatterns( select ), + ] +); + const patternBlockToPattern = ( patternBlock, categories ) => ( { blocks: parse( patternBlock.content.raw, { __unstableSkipMigrationLogs: true, @@ -164,44 +214,65 @@ const patternBlockToPattern = ( patternBlock, categories ) => ( { patternBlock, } ); -const selectUserPatterns = ( select, { search = '', syncStatus } = {} ) => { - const { getEntityRecords, getIsResolving, getUserPatternCategories } = - select( coreStore ); +const selectUserPatterns = createSelector( + ( select, syncStatus, search = '' ) => { + const { getEntityRecords, getIsResolving, getUserPatternCategories } = + select( coreStore ); - const query = { per_page: -1 }; - const records = getEntityRecords( 'postType', PATTERN_TYPES.user, query ); - const userPatternCategories = getUserPatternCategories(); - const categories = new Map(); - userPatternCategories.forEach( ( userCategory ) => - categories.set( userCategory.id, userCategory ) - ); - let patterns = records - ? records.map( ( record ) => - patternBlockToPattern( record, categories ) - ) - : EMPTY_PATTERN_LIST; - - const isResolving = getIsResolving( 'getEntityRecords', [ - 'postType', - PATTERN_TYPES.user, - query, - ] ); - - if ( syncStatus ) { - patterns = patterns.filter( - ( pattern ) => pattern.syncStatus === syncStatus + const query = { per_page: -1 }; + const records = getEntityRecords( + 'postType', + PATTERN_TYPES.user, + query ); - } + const userPatternCategories = getUserPatternCategories(); + const categories = new Map(); + userPatternCategories.forEach( ( userCategory ) => + categories.set( userCategory.id, userCategory ) + ); + let patterns = records + ? records.map( ( record ) => + patternBlockToPattern( record, categories ) + ) + : EMPTY_PATTERN_LIST; - patterns = searchItems( patterns, search, { - // We exit user pattern retrieval early if we aren't in the - // catch-all category for user created patterns, so it has - // to be in the category. - hasCategory: () => true, - } ); + const isResolving = getIsResolving( 'getEntityRecords', [ + 'postType', + PATTERN_TYPES.user, + query, + ] ); - return { patterns, isResolving, categories: userPatternCategories }; -}; + if ( syncStatus ) { + patterns = patterns.filter( + ( pattern ) => pattern.syncStatus === syncStatus + ); + } + + patterns = searchItems( patterns, search, { + // We exit user pattern retrieval early if we aren't in the + // catch-all category for user created patterns, so it has + // to be in the category. + hasCategory: () => true, + } ); + + return { + patterns, + isResolving, + categories: userPatternCategories, + }; + }, + ( select ) => [ + select( coreStore ).getEntityRecords( 'postType', PATTERN_TYPES.user, { + per_page: -1, + } ), + select( coreStore ).getIsResolving( 'getEntityRecords', [ + 'postType', + PATTERN_TYPES.user, + { per_page: -1 }, + ] ), + select( coreStore ).getUserPatternCategories(), + ] +); export const usePatterns = ( categoryType, @@ -211,20 +282,20 @@ export const usePatterns = ( return useSelect( ( select ) => { if ( categoryType === TEMPLATE_PART_POST_TYPE ) { - return selectTemplatePartsAsPatterns( select, { + return selectTemplatePartsAsPatterns( + select, categoryId, - search, - } ); + search + ); } else if ( categoryType === PATTERN_TYPES.theme ) { - return selectPatterns( select, { - categoryId, - search, - syncStatus, - } ); + return selectPatterns( select, categoryId, syncStatus, search ); } else if ( categoryType === PATTERN_TYPES.user ) { - return selectUserPatterns( select, { search, syncStatus } ); + return selectUserPatterns( select, syncStatus, search ); } - return { patterns: EMPTY_PATTERN_LIST, isResolving: false }; + return { + patterns: EMPTY_PATTERN_LIST, + isResolving: false, + }; }, [ categoryId, categoryType, search, syncStatus ] );