From 2c509055c133e9d187138d36795f71aa0ceaab59 Mon Sep 17 00:00:00 2001 From: Jarda Snajdr Date: Wed, 18 Oct 2023 14:15:51 +0200 Subject: [PATCH] useAvailableAlignments: avoid useSelect subscription when alignments array is empty --- .../use-available-alignments.js | 83 +++++++++++-------- packages/block-editor/src/hooks/align.js | 62 +++++++------- 2 files changed, 81 insertions(+), 64 deletions(-) diff --git a/packages/block-editor/src/components/block-alignment-control/use-available-alignments.js b/packages/block-editor/src/components/block-alignment-control/use-available-alignments.js index d77eb2af2a5976..67e92b012a53bf 100644 --- a/packages/block-editor/src/components/block-alignment-control/use-available-alignments.js +++ b/packages/block-editor/src/components/block-alignment-control/use-available-alignments.js @@ -19,29 +19,43 @@ export default function useAvailableAlignments( controls = DEFAULT_CONTROLS ) { if ( ! controls.includes( 'none' ) ) { controls = [ 'none', ...controls ]; } - const { - wideControlsEnabled = false, - themeSupportsLayout, - isBlockBasedTheme, - } = useSelect( ( select ) => { - const { getSettings } = select( blockEditorStore ); - const settings = getSettings(); - return { - wideControlsEnabled: settings.alignWide, - themeSupportsLayout: settings.supportsLayout, - isBlockBasedTheme: settings.__unstableIsBlockBasedTheme, - }; - }, [] ); + const isNoneOnly = controls.length === 1 && controls[ 0 ] === 'none'; + + const [ wideControlsEnabled, themeSupportsLayout, isBlockBasedTheme ] = + useSelect( + ( select ) => { + // If `isNoneOnly` is true, we'll be returning early because there is + // nothing to filter on an empty array. We won't need the info from + // the `useSelect` but we must call it anyway because Rules of Hooks. + // So the callback returns early to avoid block editor subscription. + if ( isNoneOnly ) { + return [ false, false, false ]; + } + + const settings = select( blockEditorStore ).getSettings(); + return [ + settings.alignWide ?? false, + settings.supportsLayout, + settings.__unstableIsBlockBasedTheme, + ]; + }, + [ isNoneOnly ] + ); const layout = useLayout(); + + if ( isNoneOnly ) { + return EMPTY_ARRAY; + } + const layoutType = getLayoutType( layout?.type ); - const layoutAlignments = layoutType.getAlignments( - layout, - isBlockBasedTheme - ); if ( themeSupportsLayout ) { - const alignments = layoutAlignments.filter( - ( { name: alignmentName } ) => controls.includes( alignmentName ) + const layoutAlignments = layoutType.getAlignments( + layout, + isBlockBasedTheme + ); + const alignments = layoutAlignments.filter( ( alignment ) => + controls.includes( alignment.name ) ); // While we treat `none` as an alignment, we shouldn't return it if no // other alignments exist. @@ -55,25 +69,26 @@ export default function useAvailableAlignments( controls = DEFAULT_CONTROLS ) { if ( layoutType.name !== 'default' && layoutType.name !== 'constrained' ) { return EMPTY_ARRAY; } - const { alignments: availableAlignments = DEFAULT_CONTROLS } = layout; - const enabledControls = controls - .filter( - ( control ) => - ( layout.alignments || // Ignore the global wideAlignment check if the layout explicitely defines alignments. - wideControlsEnabled || - ! WIDE_CONTROLS.includes( control ) ) && - availableAlignments.includes( control ) - ) - .map( ( enabledControl ) => ( { name: enabledControl } ) ); + + const alignments = controls + .filter( ( control ) => { + if ( layout.alignments ) { + return layout.alignments.includes( control ); + } + + if ( ! wideControlsEnabled && WIDE_CONTROLS.includes( control ) ) { + return false; + } + + return DEFAULT_CONTROLS.includes( control ); + } ) + .map( ( name ) => ( { name } ) ); // While we treat `none` as an alignment, we shouldn't return it if no // other alignments exist. - if ( - enabledControls.length === 1 && - enabledControls[ 0 ].name === 'none' - ) { + if ( alignments.length === 1 && alignments[ 0 ].name === 'none' ) { return EMPTY_ARRAY; } - return enabledControls; + return alignments; } diff --git a/packages/block-editor/src/hooks/align.js b/packages/block-editor/src/hooks/align.js index a417d11d900431..f266bb32e18197 100644 --- a/packages/block-editor/src/hooks/align.js +++ b/packages/block-editor/src/hooks/align.js @@ -120,20 +120,17 @@ export const withToolbarControls = createHigherOrderComponent( ( BlockEdit ) => ( props ) => { const blockEdit = ; const { name: blockName } = props; - // Compute the block valid alignments by taking into account, - // if the theme supports wide alignments or not and the layout's - // availble alignments. We do that for conditionally rendering - // Slot. + + const blockEditingMode = useBlockEditingMode(); + if ( blockEditingMode !== 'default' ) { + return blockEdit; + } + const blockAllowedAlignments = getValidAlignments( getBlockSupport( blockName, 'align' ), hasBlockSupport( blockName, 'alignWide', true ) ); - - const validAlignments = useAvailableAlignments( - blockAllowedAlignments - ).map( ( { name } ) => name ); - const blockEditingMode = useBlockEditingMode(); - if ( ! validAlignments.length || blockEditingMode !== 'default' ) { + if ( blockAllowedAlignments.length === 0 ) { return blockEdit; } @@ -154,7 +151,7 @@ export const withToolbarControls = createHigherOrderComponent( { blockEdit } @@ -164,6 +161,23 @@ export const withToolbarControls = createHigherOrderComponent( 'withToolbarControls' ); +function BlockListBlockWithDataAlign( { block: BlockListBlock, props } ) { + const { name, attributes } = props; + const { align } = attributes; + const blockAllowedAlignments = getValidAlignments( + getBlockSupport( name, 'align' ), + hasBlockSupport( name, 'alignWide', true ) + ); + const validAlignments = useAvailableAlignments( blockAllowedAlignments ); + + let wrapperProps = props.wrapperProps; + if ( validAlignments.some( ( alignment ) => alignment.name === align ) ) { + wrapperProps = { ...wrapperProps, 'data-align': align }; + } + + return ; +} + /** * Override the default block element to add alignment wrapper props. * @@ -173,30 +187,18 @@ export const withToolbarControls = createHigherOrderComponent( */ export const withDataAlign = createHigherOrderComponent( ( BlockListBlock ) => ( props ) => { - const { name, attributes } = props; - const { align } = attributes; - const blockAllowedAlignments = getValidAlignments( - getBlockSupport( name, 'align' ), - hasBlockSupport( name, 'alignWide', true ) - ); - const validAlignments = useAvailableAlignments( - blockAllowedAlignments - ); - // If an alignment is not assigned, there's no need to go through the // effort to validate or assign its value. - if ( align === undefined ) { + if ( props.attributes.align === undefined ) { return ; } - let wrapperProps = props.wrapperProps; - if ( - validAlignments.some( ( alignment ) => alignment.name === align ) - ) { - wrapperProps = { ...wrapperProps, 'data-align': align }; - } - - return ; + return ( + + ); }, 'withDataAlign' );