diff --git a/packages/block-editor/src/components/block-tools/index.js b/packages/block-editor/src/components/block-tools/index.js
index 86597a03cde2a5..41a40d48d20b20 100644
--- a/packages/block-editor/src/components/block-tools/index.js
+++ b/packages/block-editor/src/components/block-tools/index.js
@@ -26,7 +26,7 @@ import BlockToolbarPopover from './block-toolbar-popover';
import { store as blockEditorStore } from '../../store';
import usePopoverScroll from '../block-popover/use-popover-scroll';
import ZoomOutModeInserters from './zoom-out-mode-inserters';
-import { useShowBlockTools } from './use-show-block-tools';
+import { shouldShowBlockTools } from './should-show-block-tools';
import { unlock } from '../../lock-unlock';
import { cleanEmptyObject } from '../../hooks/utils';
import usePasteStyles from '../use-paste-styles';
@@ -39,17 +39,47 @@ function selector( select ) {
isTyping,
isDragging,
isZoomOut,
+ getBlocksByClientId,
+ getSelectedBlockClientIds,
+ getBlockRootClientId,
+ isGroupable,
+ getBlockName,
+ getBlock,
+ getBlockMode,
+ isBlockInterfaceHidden,
} = unlock( select( blockEditorStore ) );
+ const { getGroupingBlockName } = select( blocksStore );
+
const clientId =
getSelectedBlockClientId() || getFirstMultiSelectedBlockClientId();
+ const _isTyping = isTyping();
+ const _hasFixedToolbar = getSettings().hasFixedToolbar;
+
+ const { showEmptyBlockSideInserter, showBlockToolbarPopover } =
+ shouldShowBlockTools( {
+ block: getBlock( clientId ),
+ blockMode: clientId ? getBlockMode( clientId ) : null,
+ isBlockInterfaceHidden: isBlockInterfaceHidden(),
+ clientId,
+ isTyping: _isTyping,
+ hasFixedToolbar: _hasFixedToolbar,
+ } );
return {
clientId,
- hasFixedToolbar: getSettings().hasFixedToolbar,
- isTyping: isTyping(),
+ hasFixedToolbar: _hasFixedToolbar,
+ isTyping: _isTyping,
isZoomOutMode: isZoomOut(),
isDragging: isDragging(),
+ showEmptyBlockSideInserter,
+ showBlockToolbarPopover,
+ getBlocksByClientId,
+ getSelectedBlockClientIds,
+ getBlockRootClientId,
+ isGroupable,
+ getBlockName,
+ getGroupingBlockName,
};
}
@@ -67,20 +97,23 @@ export default function BlockTools( {
__unstableContentRef,
...props
} ) {
- const { clientId, hasFixedToolbar, isTyping, isZoomOutMode, isDragging } =
- useSelect( selector, [] );
-
- const isMatch = useShortcutEventMatch();
const {
+ clientId,
+ hasFixedToolbar,
+ isTyping,
+ isZoomOutMode,
+ isDragging,
getBlocksByClientId,
getSelectedBlockClientIds,
getBlockRootClientId,
isGroupable,
getBlockName,
- } = useSelect( blockEditorStore );
- const { getGroupingBlockName } = useSelect( blocksStore );
- const { showEmptyBlockSideInserter, showBlockToolbarPopover } =
- useShowBlockTools();
+ getGroupingBlockName,
+ showEmptyBlockSideInserter,
+ showBlockToolbarPopover,
+ } = useSelect( selector, [] );
+
+ const isMatch = useShortcutEventMatch();
const pasteStyles = usePasteStyles();
const {
diff --git a/packages/block-editor/src/components/block-tools/insertion-point.js b/packages/block-editor/src/components/block-tools/insertion-point.js
index 55d6a2a4864fa3..b69772f6c8641a 100644
--- a/packages/block-editor/src/components/block-tools/insertion-point.js
+++ b/packages/block-editor/src/components/block-tools/insertion-point.js
@@ -45,6 +45,7 @@ function InbetweenInsertionPointPopover( {
isInserterShown,
isDistractionFree,
isZoomOutMode,
+ getBlockEditingMode,
} = useSelect( ( select ) => {
const {
getBlockOrder,
@@ -55,6 +56,7 @@ function InbetweenInsertionPointPopover( {
getNextBlockClientId,
getSettings,
isZoomOut,
+ getBlockEditingMode: _getBlockEditingMode,
} = unlock( select( blockEditorStore ) );
const insertionPoint = getBlockInsertionPoint();
const order = getBlockOrder( insertionPoint.rootClientId );
@@ -86,9 +88,9 @@ function InbetweenInsertionPointPopover( {
isDistractionFree: settings.isDistractionFree,
isInserterShown: insertionPoint?.__unstableWithInserter,
isZoomOutMode: isZoomOut(),
+ getBlockEditingMode: _getBlockEditingMode,
};
}, [] );
- const { getBlockEditingMode } = useSelect( blockEditorStore );
const disableMotion = useReducedMotion();
diff --git a/packages/block-editor/src/components/block-tools/should-show-block-tools.js b/packages/block-editor/src/components/block-tools/should-show-block-tools.js
new file mode 100644
index 00000000000000..c30cb3d212bd22
--- /dev/null
+++ b/packages/block-editor/src/components/block-tools/should-show-block-tools.js
@@ -0,0 +1,46 @@
+/**
+ * WordPress dependencies
+ */
+import { isUnmodifiedDefaultBlock } from '@wordpress/blocks';
+
+/**
+ * Determines which block tools should be showing based on the current editor state.
+ * This is a pure function that can be called from within a selector.
+ *
+ * @param {Object} options Calculation options.
+ * @param {Object} options.block The selected block object.
+ * @param {string} options.blockMode The block mode ('visual' or 'html').
+ * @param {boolean} options.isBlockInterfaceHidden Whether the block interface is hidden.
+ * @param {string} options.clientId The selected block client ID.
+ * @param {boolean} options.isTyping Whether the user is currently typing.
+ * @param {boolean} options.hasFixedToolbar Whether the toolbar is fixed.
+ *
+ * @return {Object} Object of which block tools will be shown.
+ */
+export function shouldShowBlockTools( {
+ block,
+ blockMode,
+ isBlockInterfaceHidden,
+ clientId,
+ isTyping,
+ hasFixedToolbar,
+} ) {
+ const hasSelectedBlock = !! clientId && !! block;
+ const isEmptyDefaultBlock =
+ hasSelectedBlock &&
+ isUnmodifiedDefaultBlock( block, 'content' ) &&
+ blockMode !== 'html';
+ const showEmptyBlockSideInserter =
+ clientId && ! isTyping && isEmptyDefaultBlock;
+ const showBlockToolbarPopover =
+ ! isBlockInterfaceHidden &&
+ ! hasFixedToolbar &&
+ ! showEmptyBlockSideInserter &&
+ hasSelectedBlock &&
+ ! isEmptyDefaultBlock;
+
+ return {
+ showEmptyBlockSideInserter,
+ showBlockToolbarPopover,
+ };
+}
diff --git a/packages/block-editor/src/components/block-tools/use-show-block-tools.js b/packages/block-editor/src/components/block-tools/use-show-block-tools.js
index 2b9236bb25b953..fb4b78f24a2b0d 100644
--- a/packages/block-editor/src/components/block-tools/use-show-block-tools.js
+++ b/packages/block-editor/src/components/block-tools/use-show-block-tools.js
@@ -1,53 +1,46 @@
/**
* WordPress dependencies
*/
-import { useSelect } from '@wordpress/data';
import { isUnmodifiedDefaultBlock } from '@wordpress/blocks';
/**
- * Internal dependencies
- */
-import { store as blockEditorStore } from '../../store';
-import { unlock } from '../../lock-unlock';
-
-/**
- * Source of truth for which block tools are showing in the block editor.
+ * Calculates which block tools should be showing based on the current editor state.
+ * This is a pure function that can be called from within a selector.
+ *
+ * @param {Object} options Calculation options.
+ * @param {Object} options.block The selected block object.
+ * @param {string} options.blockMode The block mode ('visual' or 'html').
+ * @param {boolean} options.isBlockInterfaceHidden Whether the block interface is hidden.
+ * @param {string} options.clientId The selected block client ID.
+ * @param {boolean} options.isTyping Whether the user is currently typing.
+ * @param {boolean} options.hasFixedToolbar Whether the toolbar is fixed.
*
* @return {Object} Object of which block tools will be shown.
*/
-export function useShowBlockTools() {
- return useSelect( ( select ) => {
- const {
- getSelectedBlockClientId,
- getFirstMultiSelectedBlockClientId,
- getBlock,
- getBlockMode,
- getSettings,
- isTyping,
- isBlockInterfaceHidden,
- } = unlock( select( blockEditorStore ) );
-
- const clientId =
- getSelectedBlockClientId() || getFirstMultiSelectedBlockClientId();
-
- const block = getBlock( clientId );
- const hasSelectedBlock = !! clientId && !! block;
- const isEmptyDefaultBlock =
- hasSelectedBlock &&
- isUnmodifiedDefaultBlock( block, 'content' ) &&
- getBlockMode( clientId ) !== 'html';
- const _showEmptyBlockSideInserter =
- clientId && ! isTyping() && isEmptyDefaultBlock;
- const _showBlockToolbarPopover =
- ! isBlockInterfaceHidden() &&
- ! getSettings().hasFixedToolbar &&
- ! _showEmptyBlockSideInserter &&
- hasSelectedBlock &&
- ! isEmptyDefaultBlock;
+export function calculateShowBlockTools( {
+ block,
+ blockMode,
+ isBlockInterfaceHidden,
+ clientId,
+ isTyping,
+ hasFixedToolbar,
+} ) {
+ const hasSelectedBlock = !! clientId && !! block;
+ const isEmptyDefaultBlock =
+ hasSelectedBlock &&
+ isUnmodifiedDefaultBlock( block, 'content' ) &&
+ blockMode !== 'html';
+ const showEmptyBlockSideInserter =
+ clientId && ! isTyping && isEmptyDefaultBlock;
+ const showBlockToolbarPopover =
+ ! isBlockInterfaceHidden &&
+ ! hasFixedToolbar &&
+ ! showEmptyBlockSideInserter &&
+ hasSelectedBlock &&
+ ! isEmptyDefaultBlock;
- return {
- showEmptyBlockSideInserter: _showEmptyBlockSideInserter,
- showBlockToolbarPopover: _showBlockToolbarPopover,
- };
- }, [] );
+ return {
+ showEmptyBlockSideInserter,
+ showBlockToolbarPopover,
+ };
}
diff --git a/packages/block-editor/src/hooks/border.js b/packages/block-editor/src/hooks/border.js
index 4ab4c69a41f311..fbce7c1880b46f 100644
--- a/packages/block-editor/src/hooks/border.js
+++ b/packages/block-editor/src/hooks/border.js
@@ -142,12 +142,18 @@ function BordersInspectorControl( { label, children, resetAllFilter } ) {
export function BorderPanel( { clientId, name, setAttributes, settings } ) {
const isEnabled = useHasBorderPanel( settings );
- function selector( select ) {
- const { style, borderColor } =
- select( blockEditorStore ).getBlockAttributes( clientId ) || {};
- return { style, borderColor };
- }
- const { style, borderColor } = useSelect( selector, [ clientId ] );
+ const { style, borderColor } = useSelect(
+ ( select ) => {
+ // Early return to avoid subscription when disabled
+ if ( ! isEnabled ) {
+ return {};
+ }
+ const { style: _style, borderColor: _borderColor } =
+ select( blockEditorStore ).getBlockAttributes( clientId ) || {};
+ return { style: _style, borderColor: _borderColor };
+ },
+ [ clientId, isEnabled ]
+ );
const value = useMemo( () => {
return attributesToStyle( { style, borderColor } );
}, [ style, borderColor ] );
diff --git a/packages/block-editor/src/hooks/color.js b/packages/block-editor/src/hooks/color.js
index ef8984c9367853..237a462dab7b14 100644
--- a/packages/block-editor/src/hooks/color.js
+++ b/packages/block-editor/src/hooks/color.js
@@ -262,14 +262,27 @@ function ColorInspectorControl( { children, resetAllFilter } ) {
export function ColorEdit( { clientId, name, setAttributes, settings } ) {
const isEnabled = useHasColorPanel( settings );
- function selector( select ) {
- const { style, textColor, backgroundColor, gradient } =
- select( blockEditorStore ).getBlockAttributes( clientId ) || {};
- return { style, textColor, backgroundColor, gradient };
- }
+
const { style, textColor, backgroundColor, gradient } = useSelect(
- selector,
- [ clientId ]
+ ( select ) => {
+ // Early return to avoid subscription when disabled
+ if ( ! isEnabled ) {
+ return {};
+ }
+ const {
+ style: _style,
+ textColor: _textColor,
+ backgroundColor: _backgroundColor,
+ gradient: _gradient,
+ } = select( blockEditorStore ).getBlockAttributes( clientId ) || {};
+ return {
+ style: _style,
+ textColor: _textColor,
+ backgroundColor: _backgroundColor,
+ gradient: _gradient,
+ };
+ },
+ [ clientId, isEnabled ]
);
const value = useMemo( () => {
return attributesToStyle( {
diff --git a/packages/block-editor/src/hooks/dimensions.js b/packages/block-editor/src/hooks/dimensions.js
index ffa4048b7740e3..941bf645413743 100644
--- a/packages/block-editor/src/hooks/dimensions.js
+++ b/packages/block-editor/src/hooks/dimensions.js
@@ -71,9 +71,15 @@ function DimensionsInspectorControl( { children, resetAllFilter } ) {
export function DimensionsPanel( { clientId, name, setAttributes, settings } ) {
const isEnabled = useHasDimensionsPanel( settings );
const value = useSelect(
- ( select ) =>
- select( blockEditorStore ).getBlockAttributes( clientId )?.style,
- [ clientId ]
+ ( select ) => {
+ // Early return to avoid subscription when disabled
+ if ( ! isEnabled ) {
+ return undefined;
+ }
+ return select( blockEditorStore ).getBlockAttributes( clientId )
+ ?.style;
+ },
+ [ clientId, isEnabled ]
);
const [ visualizedProperty, setVisualizedProperty ] = useVisualizer();
const onChange = ( newStyle ) => {
diff --git a/packages/block-editor/src/hooks/layout.js b/packages/block-editor/src/hooks/layout.js
index 003a680940b977..772b86b334eee4 100644
--- a/packages/block-editor/src/hooks/layout.js
+++ b/packages/block-editor/src/hooks/layout.js
@@ -74,13 +74,17 @@ export function useLayoutClasses( blockAttributes = {}, blockName = '' ) {
const hasGlobalPadding = useSelect(
( select ) => {
- return (
- ( usedLayout?.inherit ||
- usedLayout?.contentSize ||
- usedLayout?.type === 'constrained' ) &&
- select( blockEditorStore ).getSettings().__experimentalFeatures
- ?.useRootPaddingAwareAlignments
- );
+ // Early return to avoid subscription when layout doesn't use global padding
+ if (
+ ! usedLayout?.inherit &&
+ ! usedLayout?.contentSize &&
+ usedLayout?.type !== 'constrained'
+ ) {
+ return false;
+ }
+
+ return select( blockEditorStore ).getSettings()
+ .__experimentalFeatures?.useRootPaddingAwareAlignments;
},
[ usedLayout?.contentSize, usedLayout?.inherit, usedLayout?.type ]
);
diff --git a/packages/block-editor/src/hooks/typography.js b/packages/block-editor/src/hooks/typography.js
index 5c551a4bb35761..b19582911a778e 100644
--- a/packages/block-editor/src/hooks/typography.js
+++ b/packages/block-editor/src/hooks/typography.js
@@ -115,15 +115,29 @@ function TypographyInspectorControl( { children, resetAllFilter } ) {
}
export function TypographyPanel( { clientId, name, setAttributes, settings } ) {
- function selector( select ) {
- const { style, fontFamily, fontSize, fitText } =
- select( blockEditorStore ).getBlockAttributes( clientId ) || {};
- return { style, fontFamily, fontSize, fitText };
- }
- const { style, fontFamily, fontSize, fitText } = useSelect( selector, [
- clientId,
- ] );
const isEnabled = useHasTypographyPanel( settings );
+
+ const { style, fontFamily, fontSize, fitText } = useSelect(
+ ( select ) => {
+ // Early return INSIDE selector to avoid subscription when disabled
+ if ( ! isEnabled ) {
+ return {};
+ }
+ const {
+ style: _style,
+ fontFamily: _fontFamily,
+ fontSize: _fontSize,
+ fitText: _fitText,
+ } = select( blockEditorStore ).getBlockAttributes( clientId ) || {};
+ return {
+ style: _style,
+ fontFamily: _fontFamily,
+ fontSize: _fontSize,
+ fitText: _fitText,
+ };
+ },
+ [ clientId, isEnabled ]
+ );
const value = useMemo(
() => attributesToStyle( { style, fontFamily, fontSize } ),
[ style, fontSize, fontFamily ]
diff --git a/packages/edit-post/src/components/init-pattern-modal/index.js b/packages/edit-post/src/components/init-pattern-modal/index.js
index 1bfeadd73f94a6..fdc633534895ae 100644
--- a/packages/edit-post/src/components/init-pattern-modal/index.js
+++ b/packages/edit-post/src/components/init-pattern-modal/index.js
@@ -19,19 +19,14 @@ export default function InitPatternModal() {
const [ syncType, setSyncType ] = useState( undefined );
const [ title, setTitle ] = useState( '' );
- const { postType, isNewPost } = useSelect( ( select ) => {
- const { getEditedPostAttribute, isCleanNewPost } =
- select( editorStore );
- return {
- postType: getEditedPostAttribute( 'type' ),
- isNewPost: isCleanNewPost(),
- };
- }, [] );
- const [ isModalOpen, setIsModalOpen ] = useState(
- () => isNewPost && postType === 'wp_block'
+ const isNewPost = useSelect(
+ ( select ) => select( editorStore ).isCleanNewPost(),
+ []
);
- if ( postType !== 'wp_block' || ! isNewPost ) {
+ const [ isModalOpen, setIsModalOpen ] = useState( () => isNewPost );
+
+ if ( ! isNewPost ) {
return null;
}
diff --git a/packages/edit-post/src/components/layout/index.js b/packages/edit-post/src/components/layout/index.js
index 6bf2b2349d240f..61d1cef6441d28 100644
--- a/packages/edit-post/src/components/layout/index.js
+++ b/packages/edit-post/src/components/layout/index.js
@@ -673,7 +673,9 @@ function Layout( {
-
+ { currentPostType === 'wp_block' && (
+
+ ) }
{ backButton }
diff --git a/packages/editor/src/components/pattern-duplicate-modal/index.js b/packages/editor/src/components/pattern-duplicate-modal/index.js
index 2e5593536d1834..4c6ff6f83ef43f 100644
--- a/packages/editor/src/components/pattern-duplicate-modal/index.js
+++ b/packages/editor/src/components/pattern-duplicate-modal/index.js
@@ -17,25 +17,33 @@ const { DuplicatePatternModal } = unlock( patternsPrivateApis );
export const modalName = 'editor/pattern-duplicate';
export default function PatternDuplicateModal() {
- const { record, postType } = useSelect( ( select ) => {
- const { getCurrentPostType, getCurrentPostId } = select( editorStore );
- const { getEditedEntityRecord } = select( coreStore );
- const _postType = getCurrentPostType();
- return {
- record: getEditedEntityRecord(
- 'postType',
- _postType,
- getCurrentPostId()
- ),
- postType: _postType,
- };
- }, [] );
- const { closeModal } = useDispatch( interfaceStore );
-
const isActive = useSelect( ( select ) =>
select( interfaceStore ).isModalActive( modalName )
);
+ const { record, postType } = useSelect(
+ ( select ) => {
+ if ( ! isActive ) {
+ return {};
+ }
+
+ const { getCurrentPostType, getCurrentPostId } =
+ select( editorStore );
+ const { getEditedEntityRecord } = select( coreStore );
+ const _postType = getCurrentPostType();
+ return {
+ record: getEditedEntityRecord(
+ 'postType',
+ _postType,
+ getCurrentPostId()
+ ),
+ postType: _postType,
+ };
+ },
+ [ isActive ]
+ );
+ const { closeModal } = useDispatch( interfaceStore );
+
if ( ! isActive || postType !== PATTERN_POST_TYPE ) {
return null;
}
diff --git a/packages/editor/src/components/pattern-rename-modal/index.js b/packages/editor/src/components/pattern-rename-modal/index.js
index ea9a46e977b240..085efbb4cf33c4 100644
--- a/packages/editor/src/components/pattern-rename-modal/index.js
+++ b/packages/editor/src/components/pattern-rename-modal/index.js
@@ -17,25 +17,34 @@ const { RenamePatternModal } = unlock( patternsPrivateApis );
export const modalName = 'editor/pattern-rename';
export default function PatternRenameModal() {
- const { record, postType } = useSelect( ( select ) => {
- const { getCurrentPostType, getCurrentPostId } = select( editorStore );
- const { getEditedEntityRecord } = select( coreStore );
- const _postType = getCurrentPostType();
- return {
- record: getEditedEntityRecord(
- 'postType',
- _postType,
- getCurrentPostId()
- ),
- postType: _postType,
- };
- }, [] );
- const { closeModal } = useDispatch( interfaceStore );
-
const isActive = useSelect( ( select ) =>
select( interfaceStore ).isModalActive( modalName )
);
+ const { record, postType } = useSelect(
+ ( select ) => {
+ if ( ! isActive ) {
+ return {};
+ }
+
+ const { getCurrentPostType, getCurrentPostId } =
+ select( editorStore );
+ const { getEditedEntityRecord } = select( coreStore );
+ const _postType = getCurrentPostType();
+ return {
+ record: getEditedEntityRecord(
+ 'postType',
+ _postType,
+ getCurrentPostId()
+ ),
+ postType: _postType,
+ };
+ },
+ [ isActive ]
+ );
+
+ const { closeModal } = useDispatch( interfaceStore );
+
if ( ! isActive || postType !== PATTERN_POST_TYPE ) {
return null;
}