From 435fabc91bdc7de3e6cd5cef8a2be559ed9c1acf Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 27 Jan 2025 14:49:24 +0400 Subject: [PATCH 1/2] Block Editor: Fix 'isBlockVisibleInTheInserter' selector helper performance --- packages/block-editor/src/store/selectors.js | 48 +++++++++++++++----- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 22d725bbcd65de..6f0eb85d6e93cc 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -622,6 +622,33 @@ export const getBlockParents = createSelector( ( state ) => [ state.blocks.parents ] ); +/** + * Given a block client ID and a block name, returns the list of all its parents + * from top to bottom, filtered by the given name(s). + * + * The function is not exported and not memoized because + * it's not efficient to call memoized selectors inside other selectors. + * + * @param {Object} state Editor state. + * @param {string} clientId Block from which to find root client ID. + * @param {string|string[]} blockName Block name(s) to filter. + * @param {boolean} ascending Order results from bottom to top (true) or top to bottom (false). + * + * @return {Array} ClientIDs of the parent blocks. + */ +function getBlockParentsByBlockNameUnmemoized( + state, + clientId, + blockName, + ascending = false +) { + const parents = getBlockParents( state, clientId, ascending ); + const hasName = Array.isArray( blockName ) + ? ( name ) => blockName.includes( name ) + : ( name ) => blockName === name; + return parents.filter( ( id ) => hasName( getBlockName( state, id ) ) ); +} + /** * Given a block client ID and a block name, returns the list of all its parents * from top to bottom, filtered by the given name(s). For example, if passed @@ -637,13 +664,7 @@ export const getBlockParents = createSelector( * @return {Array} ClientIDs of the parent blocks. */ export const getBlockParentsByBlockName = createSelector( - ( state, clientId, blockName, ascending = false ) => { - const parents = getBlockParents( state, clientId, ascending ); - const hasName = Array.isArray( blockName ) - ? ( name ) => blockName.includes( name ) - : ( name ) => blockName === name; - return parents.filter( ( id ) => hasName( getBlockName( state, id ) ) ); - }, + getBlockParentsByBlockNameUnmemoized, ( state ) => [ state.blocks.parents ] ); /** @@ -1629,11 +1650,16 @@ const isBlockVisibleInTheInserter = ( const rootBlockName = getBlockName( state, rootClientId ); // This is an exception to the rule that says that all blocks are visible in the inserter. // Blocks that require a given parent or ancestor are only visible if we're within that parent. - return ( + if ( parents.includes( 'core/post-content' ) || - parents.includes( rootBlockName ) || - getBlockParentsByBlockName( state, rootClientId, parents ).length > - 0 + parents.includes( rootBlockName ) + ) { + return true; + } + + return ( + getBlockParentsByBlockNameUnmemoized( state, rootClientId, parents ) + .length > 0 ); } From 507965f41154acd08ddebc3c2fbdd7838688a94c Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Mon, 27 Jan 2025 17:17:13 +0400 Subject: [PATCH 2/2] Use the loop, Luke! --- packages/block-editor/src/store/selectors.js | 56 +++++++------------- 1 file changed, 19 insertions(+), 37 deletions(-) diff --git a/packages/block-editor/src/store/selectors.js b/packages/block-editor/src/store/selectors.js index 6f0eb85d6e93cc..e2e2ef6b655564 100644 --- a/packages/block-editor/src/store/selectors.js +++ b/packages/block-editor/src/store/selectors.js @@ -622,33 +622,6 @@ export const getBlockParents = createSelector( ( state ) => [ state.blocks.parents ] ); -/** - * Given a block client ID and a block name, returns the list of all its parents - * from top to bottom, filtered by the given name(s). - * - * The function is not exported and not memoized because - * it's not efficient to call memoized selectors inside other selectors. - * - * @param {Object} state Editor state. - * @param {string} clientId Block from which to find root client ID. - * @param {string|string[]} blockName Block name(s) to filter. - * @param {boolean} ascending Order results from bottom to top (true) or top to bottom (false). - * - * @return {Array} ClientIDs of the parent blocks. - */ -function getBlockParentsByBlockNameUnmemoized( - state, - clientId, - blockName, - ascending = false -) { - const parents = getBlockParents( state, clientId, ascending ); - const hasName = Array.isArray( blockName ) - ? ( name ) => blockName.includes( name ) - : ( name ) => blockName === name; - return parents.filter( ( id ) => hasName( getBlockName( state, id ) ) ); -} - /** * Given a block client ID and a block name, returns the list of all its parents * from top to bottom, filtered by the given name(s). For example, if passed @@ -664,7 +637,13 @@ function getBlockParentsByBlockNameUnmemoized( * @return {Array} ClientIDs of the parent blocks. */ export const getBlockParentsByBlockName = createSelector( - getBlockParentsByBlockNameUnmemoized, + ( state, clientId, blockName, ascending = false ) => { + const parents = getBlockParents( state, clientId, ascending ); + const hasName = Array.isArray( blockName ) + ? ( name ) => blockName.includes( name ) + : ( name ) => blockName === name; + return parents.filter( ( id ) => hasName( getBlockName( state, id ) ) ); + }, ( state ) => [ state.blocks.parents ] ); /** @@ -1647,20 +1626,23 @@ const isBlockVisibleInTheInserter = ( Array.isArray( blockType.parent ) ? blockType.parent : [] ).concat( Array.isArray( blockType.ancestor ) ? blockType.ancestor : [] ); if ( parents.length > 0 ) { - const rootBlockName = getBlockName( state, rootClientId ); // This is an exception to the rule that says that all blocks are visible in the inserter. // Blocks that require a given parent or ancestor are only visible if we're within that parent. - if ( - parents.includes( 'core/post-content' ) || - parents.includes( rootBlockName ) - ) { + if ( parents.includes( 'core/post-content' ) ) { return true; } - return ( - getBlockParentsByBlockNameUnmemoized( state, rootClientId, parents ) - .length > 0 - ); + let current = rootClientId; + let hasParent = false; + do { + if ( parents.includes( getBlockName( state, current ) ) ) { + hasParent = true; + break; + } + current = state.blocks.parents.get( current ); + } while ( current ); + + return hasParent; } return true;