From aa938166f28bcfff6c7b8d1caac5a95f78852f97 Mon Sep 17 00:00:00 2001 From: Kai Hao Date: Fri, 22 Sep 2023 15:36:24 +0800 Subject: [PATCH] Memoize `useSelect` for `usePatterns` (#54588) * Memoize useSelect for usePatterns * Revert memoized selector in resolver --- .../components/page-patterns/use-patterns.js | 320 ++++++++++-------- 1 file changed, 187 insertions(+), 133 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 c3fb7d17a7c003..1558f4b1e00320 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 */ @@ -43,108 +48,136 @@ 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 ) - ); +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 ) + ); - // 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 ); + // 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 !== TEMPLATE_PART_AREA_DEFAULT_CATEGORY ) { - return item.templatePart.area === category; - } + const templatePartHasCategory = ( item, category ) => { + if ( category !== TEMPLATE_PART_AREA_DEFAULT_CATEGORY ) { + return item.templatePart.area === category; + } - return ( - item.templatePart.area === category || - ! templatePartAreas.includes( item.templatePart.area ) - ); - }; + return ( + item.templatePart.area === category || + ! templatePartAreas.includes( item.templatePart.area ) + ); + }; + + const isResolving = getIsResolving( 'getEntityRecords', [ + 'postType', + TEMPLATE_PART_POST_TYPE, + query, + ] ); - const isResolving = getIsResolving( 'getEntityRecords', [ - 'postType', - TEMPLATE_PART_POST_TYPE, - query, - ] ); + const patterns = searchItems( templateParts, search, { + categoryId, + hasCategory: templatePartHasCategory, + } ); - const patterns = searchItems( templateParts, search, { - categoryId, - hasCategory: templatePartHasCategory, - } ); + 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(), + ] +); - return { patterns, isResolving }; -}; +const selectThemePatterns = createSelector( + ( select ) => { + const { getSettings } = unlock( select( editSiteStore ) ); + const settings = getSettings(); + const blockPatterns = + settings.__experimentalAdditionalBlockPatterns ?? + settings.__experimentalBlockPatterns; -const selectThemePatterns = ( select ) => { - const { getSettings } = unlock( select( editSiteStore ) ); - const settings = getSettings(); - const blockPatterns = - settings.__experimentalAdditionalBlockPatterns ?? - settings.__experimentalBlockPatterns; + const restBlockPatterns = select( coreStore ).getBlockPatterns(); - 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, + } ), + } ) ); - const patterns = [ - ...( blockPatterns || [] ), - ...( restBlockPatterns || [] ), + return { patterns, isResolving: false }; + }, + ( select ) => [ + select( coreStore ).getBlockPatterns(), + unlock( select( editSiteStore ) ).getSettings(), ] - .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 ); +); - let patterns = [ ...( themePatterns || [] ), ...( userPatterns || [] ) ]; +const selectPatterns = createSelector( + ( select, categoryId, syncStatus, search = '' ) => { + const { patterns: themePatterns } = selectThemePatterns( select ); + const { patterns: userPatterns } = selectUserPatterns( select ); - if ( syncStatus ) { - patterns = patterns.filter( - ( pattern ) => pattern.syncStatus === syncStatus - ); - } + let patterns = [ + ...( themePatterns || [] ), + ...( userPatterns || [] ), + ]; - 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 }; -}; + 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, { @@ -166,44 +199,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, @@ -213,20 +267,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 ] );