From 9fef8fb128ef280ed5348c4f4ba3ec93c883fe6a Mon Sep 17 00:00:00 2001 From: Mario Santos Date: Tue, 24 Sep 2024 12:33:01 +0200 Subject: [PATCH] Split select in getFieldsList hook --- .../block-editor/src/hooks/block-bindings.js | 87 +++++++++---------- 1 file changed, 43 insertions(+), 44 deletions(-) diff --git a/packages/block-editor/src/hooks/block-bindings.js b/packages/block-editor/src/hooks/block-bindings.js index ea0d4cbb7fb5bf..64d0a9bb4e6ddc 100644 --- a/packages/block-editor/src/hooks/block-bindings.js +++ b/packages/block-editor/src/hooks/block-bindings.js @@ -31,8 +31,6 @@ import { store as blockEditorStore } from '../store'; const { DropdownMenuV2 } = unlock( componentsPrivateApis ); -const EMPTY_OBJECT = {}; - const useToolsPanelDropdownMenuProps = () => { const isMobile = useViewportMatch( 'medium', '<' ); return ! isMobile @@ -192,50 +190,51 @@ export const BlockBindingsPanel = ( { name: blockName, metadata } ) => { const bindableAttributes = getBindableAttributes( blockName ); const dropdownMenuProps = useToolsPanelDropdownMenuProps(); - // `useSelect` is used purposely here to ensure `getFieldsList` - // is updated whenever there are updates in block context. - // `source.getFieldsList` may also call a selector via `registry.select`. - const _fieldsList = {}; - const { fieldsList, canUpdateBlockBindings } = useSelect( - ( select ) => { - if ( ! bindableAttributes || bindableAttributes.length === 0 ) { - return EMPTY_OBJECT; - } - const { getBlockBindingsSources } = unlock( blocksPrivateApis ); - const registeredSources = getBlockBindingsSources(); - Object.entries( registeredSources ).forEach( - ( [ sourceName, { getFieldsList, usesContext } ] ) => { - if ( getFieldsList ) { - // Populate context. - const context = {}; - if ( usesContext?.length ) { - for ( const key of usesContext ) { - context[ key ] = blockContext[ key ]; - } - } - const sourceList = getFieldsList( { - registry, - context, - } ); - // Only add source if the list is not empty. - if ( Object.keys( sourceList || {} ).length ) { - _fieldsList[ sourceName ] = { ...sourceList }; + const { canUpdateBlockBindings } = useSelect( ( select ) => { + return { + canUpdateBlockBindings: + select( blockEditorStore ).getSettings().canUpdateBlockBindings, + }; + }, [] ); + + /** + * Create new selector for fieldsList to avoid unnecessary re-renders. + * See: https://github.com/WordPress/gutenberg/pull/64072#discussion_r1764693730 + * + * `useSelect` is used purposely here to ensure `getFieldsList` is updated + * whenever there are updates in block context. + * `source.getFieldsList` may also call a selector via `registry.select`. + */ + const fieldsList = useSelect( () => { + if ( ! bindableAttributes || bindableAttributes.length === 0 ) { + return; + } + const _fieldsList = {}; + const { getBlockBindingsSources } = unlock( blocksPrivateApis ); + const registeredSources = getBlockBindingsSources(); + Object.entries( registeredSources ).forEach( + ( [ sourceName, { getFieldsList, usesContext } ] ) => { + if ( getFieldsList ) { + // Populate context. + const context = {}; + if ( usesContext?.length ) { + for ( const key of usesContext ) { + context[ key ] = blockContext[ key ]; } } + const sourceList = getFieldsList( { + registry, + context, + } ); + // Only add source if the list is not empty. + if ( Object.keys( sourceList || {} ).length ) { + _fieldsList[ sourceName ] = { ...sourceList }; + } } - ); - return { - fieldsList: - Object.values( _fieldsList ).length > 0 - ? _fieldsList - : EMPTY_OBJECT, - canUpdateBlockBindings: - select( blockEditorStore ).getSettings() - .canUpdateBlockBindings, - }; - }, - [ blockContext, bindableAttributes, registry ] - ); + } + ); + return Object.values( _fieldsList ).length > 0 && { ..._fieldsList }; + }, [ blockContext, bindableAttributes, registry ] ); // Return early if there are no bindable attributes. if ( ! bindableAttributes || bindableAttributes.length === 0 ) { return null; @@ -254,7 +253,7 @@ export const BlockBindingsPanel = ( { name: blockName, metadata } ) => { // Lock the UI when the user can't update bindings or there are no fields to connect to. const readOnly = - ! canUpdateBlockBindings || ! Object.keys( fieldsList ).length; + ! canUpdateBlockBindings || ! Object.keys( fieldsList || {} ).length; if ( readOnly && Object.keys( filteredBindings ).length === 0 ) { return null;