diff --git a/packages/block-editor/src/components/block-editing-mode/index.js b/packages/block-editor/src/components/block-editing-mode/index.js
index 5d916d9816e606..20f93f6b36f908 100644
--- a/packages/block-editor/src/components/block-editing-mode/index.js
+++ b/packages/block-editor/src/components/block-editing-mode/index.js
@@ -2,13 +2,13 @@
  * WordPress dependencies
  */
 import { useSelect, useDispatch } from '@wordpress/data';
-import { useContext, useEffect } from '@wordpress/element';
+import { useEffect } from '@wordpress/element';
 
 /**
  * Internal dependencies
  */
 import { store as blockEditorStore } from '../../store';
-import { BlockListBlockContext } from '../block-list/block-list-block-context';
+import { useBlockEditContext } from '../block-edit/context';
 
 /**
  * @typedef {'disabled'|'contentOnly'|'default'} BlockEditingMode
@@ -45,7 +45,7 @@ import { BlockListBlockContext } from '../block-list/block-list-block-context';
  * @return {BlockEditingMode} The current editing mode.
  */
 export function useBlockEditingMode( mode ) {
-	const { clientId = '' } = useContext( BlockListBlockContext ) ?? {};
+	const { clientId = '' } = useBlockEditContext();
 	const blockEditingMode = useSelect(
 		( select ) =>
 			select( blockEditorStore ).getBlockEditingMode( clientId ),
diff --git a/packages/block-editor/src/components/block-list/block.js b/packages/block-editor/src/components/block-list/block.js
index b38dcf3ef1f2ec..0bd5d0b7e199f8 100644
--- a/packages/block-editor/src/components/block-list/block.js
+++ b/packages/block-editor/src/components/block-list/block.js
@@ -6,7 +6,7 @@ import classnames from 'classnames';
 /**
  * WordPress dependencies
  */
-import { useMemo, useCallback, RawHTML } from '@wordpress/element';
+import { useCallback, RawHTML, useContext } from '@wordpress/element';
 import {
 	getBlockType,
 	getSaveContent,
@@ -15,16 +15,13 @@ import {
 	switchToBlockType,
 	getDefaultBlockName,
 	isUnmodifiedBlock,
+	isReusableBlock,
+	getBlockDefaultClassName,
 	store as blocksStore,
 } from '@wordpress/blocks';
 import { withFilters } from '@wordpress/components';
-import {
-	withDispatch,
-	withSelect,
-	useDispatch,
-	useSelect,
-} from '@wordpress/data';
-import { compose, pure, ifCondition } from '@wordpress/compose';
+import { withDispatch, useDispatch, useSelect } from '@wordpress/data';
+import { compose, pure } from '@wordpress/compose';
 import { safeHTML } from '@wordpress/dom';
 
 /**
@@ -38,7 +35,15 @@ import BlockHtml from './block-html';
 import { useBlockProps } from './use-block-props';
 import { store as blockEditorStore } from '../../store';
 import { useLayout } from './layout';
-import { BlockListBlockContext } from './block-list-block-context';
+import { PrivateBlockContext } from './private-block-context';
+
+import { unlock } from '../../lock-unlock';
+
+/**
+ * If the block count exceeds the threshold, we disable the reordering animation
+ * to avoid laginess.
+ */
+const BLOCK_ANIMATION_THRESHOLD = 200;
 
 /**
  * Merges wrapper props with special handling for classNames and styles.
@@ -101,44 +106,11 @@ function BlockListBlock( {
 	toggleSelection,
 } ) {
 	const {
-		themeSupportsLayout,
-		isTemporarilyEditingAsBlocks,
-		blockEditingMode,
 		mayDisplayControls,
 		mayDisplayParentControls,
-	} = useSelect(
-		( select ) => {
-			const {
-				getSettings,
-				__unstableGetTemporarilyEditingAsBlocks,
-				getBlockEditingMode,
-				getBlockName,
-				isFirstMultiSelectedBlock,
-				getMultiSelectedBlockClientIds,
-				hasSelectedInnerBlock,
-			} = select( blockEditorStore );
-			const { hasBlockSupport } = select( blocksStore );
-			return {
-				themeSupportsLayout: getSettings().supportsLayout,
-				isTemporarilyEditingAsBlocks:
-					__unstableGetTemporarilyEditingAsBlocks() === clientId,
-				blockEditingMode: getBlockEditingMode( clientId ),
-				mayDisplayControls:
-					isSelected ||
-					( isFirstMultiSelectedBlock( clientId ) &&
-						getMultiSelectedBlockClientIds().every(
-							( id ) => getBlockName( id ) === name
-						) ),
-				mayDisplayParentControls:
-					hasBlockSupport(
-						getBlockName( clientId ),
-						'__experimentalExposeControlsToChildren',
-						false
-					) && hasSelectedInnerBlock( clientId ),
-			};
-		},
-		[ clientId, isSelected, name ]
-	);
+		themeSupportsLayout,
+		...context
+	} = useContext( PrivateBlockContext );
 	const { removeBlock } = useDispatch( blockEditorStore );
 	const onRemove = useCallback( () => removeBlock( clientId ), [ clientId ] );
 
@@ -172,12 +144,6 @@ function BlockListBlock( {
 
 	const blockType = getBlockType( name );
 
-	if ( blockEditingMode === 'disabled' ) {
-		wrapperProps = {
-			...wrapperProps,
-			tabIndex: -1,
-		};
-	}
 	// Determine whether the block has props to apply to the wrapper.
 	if ( blockType?.getEditWrapperProps ) {
 		wrapperProps = mergeWrapperProps(
@@ -241,30 +207,28 @@ function BlockListBlock( {
 	} else if ( blockType?.apiVersion > 1 ) {
 		block = blockEdit;
 	} else {
-		block = <Block { ...wrapperProps }>{ blockEdit }</Block>;
+		block = <Block>{ blockEdit }</Block>;
 	}
 
 	const { 'data-align': dataAlign, ...restWrapperProps } = wrapperProps ?? {};
 
-	const value = {
-		clientId,
-		className: classnames(
-			{
-				'is-editing-disabled': blockEditingMode === 'disabled',
-				'is-content-locked-temporarily-editing-as-blocks':
-					isTemporarilyEditingAsBlocks,
-			},
-			dataAlign && themeSupportsLayout && `align${ dataAlign }`,
-			! ( dataAlign && isSticky ) && className
-		),
-		wrapperProps: restWrapperProps,
-		isAligned,
-	};
-
-	const memoizedValue = useMemo( () => value, Object.values( value ) );
+	restWrapperProps.className = classnames(
+		restWrapperProps.className,
+		dataAlign && themeSupportsLayout && `align${ dataAlign }`,
+		! ( dataAlign && isSticky ) && className
+	);
 
+	// We set a new context with the adjusted and filtered wrapperProps (through
+	// `editor.BlockListBlock`), which the `BlockListBlockProvider` did not have
+	// access to.
 	return (
-		<BlockListBlockContext.Provider value={ memoizedValue }>
+		<PrivateBlockContext.Provider
+			value={ {
+				wrapperProps: restWrapperProps,
+				isAligned,
+				...context,
+			} }
+		>
 			<BlockCrashBoundary
 				fallback={
 					<Block className="has-warning">
@@ -274,52 +238,10 @@ function BlockListBlock( {
 			>
 				{ block }
 			</BlockCrashBoundary>
-		</BlockListBlockContext.Provider>
+		</PrivateBlockContext.Provider>
 	);
 }
 
-const applyWithSelect = withSelect( ( select, { clientId, rootClientId } ) => {
-	const {
-		isBlockSelected,
-		getBlockMode,
-		isSelectionEnabled,
-		getTemplateLock,
-		__unstableGetBlockWithoutInnerBlocks,
-		canRemoveBlock,
-		canMoveBlock,
-	} = select( blockEditorStore );
-	const block = __unstableGetBlockWithoutInnerBlocks( clientId );
-	const isSelected = isBlockSelected( clientId );
-	const templateLock = getTemplateLock( rootClientId );
-	const canRemove = canRemoveBlock( clientId, rootClientId );
-	const canMove = canMoveBlock( clientId, rootClientId );
-
-	// The fallback to `{}` is a temporary fix.
-	// This function should never be called when a block is not present in
-	// the state. It happens now because the order in withSelect rendering
-	// is not correct.
-	const { name, attributes, isValid } = block || {};
-
-	// Do not add new properties here, use `useSelect` instead to avoid
-	// leaking new props to the public API (editor.BlockListBlock filter).
-	return {
-		mode: getBlockMode( clientId ),
-		isSelectionEnabled: isSelectionEnabled(),
-		isLocked: !! templateLock,
-		canRemove,
-		canMove,
-		// Users of the editor.BlockListBlock filter used to be able to
-		// access the block prop.
-		// Ideally these blocks would rely on the clientId prop only.
-		// This is kept for backward compatibility reasons.
-		block,
-		name,
-		attributes,
-		isValid,
-		isSelected,
-	};
-} );
-
 const applyWithDispatch = withDispatch( ( dispatch, ownProps, registry ) => {
 	const {
 		updateBlockAttributes,
@@ -558,13 +480,285 @@ const applyWithDispatch = withDispatch( ( dispatch, ownProps, registry ) => {
 	};
 } );
 
-export default compose(
-	pure,
-	applyWithSelect,
+// This component is used by the BlockListBlockProvider component below. It will
+// add the props necessary for the `editor.BlockListBlock` filters.
+BlockListBlock = compose(
 	applyWithDispatch,
-	// Block is sometimes not mounted at the right time, causing it be undefined
-	// see issue for more info
-	// https://github.com/WordPress/gutenberg/issues/17013
-	ifCondition( ( { block } ) => !! block ),
 	withFilters( 'editor.BlockListBlock' )
 )( BlockListBlock );
+
+// This component provides all the information we need through a single store
+// subscription (useSelect mapping). Only the necesssary props are passed down
+// to the BlockListBlock component, which is a filtered component, so these
+// props are public API. To avoid adding to the public API, we use a private
+// context to pass the rest of the information to the filtered BlockListBlock
+// component, and useBlockProps.
+function BlockListBlockProvider( props ) {
+	const { clientId, rootClientId } = props;
+	const selectedProps = useSelect(
+		( select ) => {
+			const {
+				isBlockSelected,
+				getBlockMode,
+				isSelectionEnabled,
+				getTemplateLock,
+				__unstableGetBlockWithoutInnerBlocks,
+				canRemoveBlock,
+				canMoveBlock,
+
+				getSettings,
+				__unstableGetTemporarilyEditingAsBlocks,
+				getBlockEditingMode,
+				getBlockName,
+				isFirstMultiSelectedBlock,
+				getMultiSelectedBlockClientIds,
+				hasSelectedInnerBlock,
+
+				getBlockIndex,
+				isTyping,
+				getGlobalBlockCount,
+				isBlockMultiSelected,
+				isAncestorMultiSelected,
+				isBlockSubtreeDisabled,
+				isBlockHighlighted,
+				__unstableIsFullySelected,
+				__unstableSelectionHasUnmergeableBlock,
+				isBlockBeingDragged,
+				hasBlockMovingClientId,
+				canInsertBlockType,
+				getBlockRootClientId,
+				__unstableHasActiveBlockOverlayActive,
+				__unstableGetEditorMode,
+				getSelectedBlocksInitialCaretPosition,
+			} = unlock( select( blockEditorStore ) );
+			const block = __unstableGetBlockWithoutInnerBlocks( clientId );
+
+			// This is a temporary fix.
+			// This function should never be called when a block is not
+			// present in the state. It happens now because the order in
+			// withSelect rendering is not correct.
+			if ( ! block ) {
+				return;
+			}
+
+			const {
+				hasBlockSupport: _hasBlockSupport,
+				getActiveBlockVariation,
+			} = select( blocksStore );
+			const _isSelected = isBlockSelected( clientId );
+			const templateLock = getTemplateLock( rootClientId );
+			const canRemove = canRemoveBlock( clientId, rootClientId );
+			const canMove = canMoveBlock( clientId, rootClientId );
+			const { name: blockName, attributes, isValid } = block;
+			const isPartOfMultiSelection =
+				isBlockMultiSelected( clientId ) ||
+				isAncestorMultiSelected( clientId );
+			const blockType = getBlockType( blockName );
+			const match = getActiveBlockVariation( blockName, attributes );
+			const { outlineMode, supportsLayout } = getSettings();
+			const isMultiSelected = isBlockMultiSelected( clientId );
+			const checkDeep = true;
+			const isAncestorOfSelectedBlock = hasSelectedInnerBlock(
+				clientId,
+				checkDeep
+			);
+			const typing = isTyping();
+			const hasLightBlockWrapper = blockType?.apiVersion > 1;
+			const movingClientId = hasBlockMovingClientId();
+
+			return {
+				mode: getBlockMode( clientId ),
+				isSelectionEnabled: isSelectionEnabled(),
+				isLocked: !! templateLock,
+				canRemove,
+				canMove,
+				// Users of the editor.BlockListBlock filter used to be able to
+				// access the block prop.
+				// Ideally these blocks would rely on the clientId prop only.
+				// This is kept for backward compatibility reasons.
+				block,
+				name: blockName,
+				attributes,
+				isValid,
+				isSelected: _isSelected,
+				themeSupportsLayout: supportsLayout,
+				isTemporarilyEditingAsBlocks:
+					__unstableGetTemporarilyEditingAsBlocks() === clientId,
+				blockEditingMode: getBlockEditingMode( clientId ),
+				mayDisplayControls:
+					_isSelected ||
+					( isFirstMultiSelectedBlock( clientId ) &&
+						getMultiSelectedBlockClientIds().every(
+							( id ) => getBlockName( id ) === blockName
+						) ),
+				mayDisplayParentControls:
+					_hasBlockSupport(
+						getBlockName( clientId ),
+						'__experimentalExposeControlsToChildren',
+						false
+					) && hasSelectedInnerBlock( clientId ),
+				index: getBlockIndex( clientId ),
+				blockApiVersion: blockType?.apiVersion || 1,
+				blockTitle: match?.title || blockType?.title,
+				isPartOfSelection: _isSelected || isPartOfMultiSelection,
+				adjustScrolling:
+					_isSelected || isFirstMultiSelectedBlock( clientId ),
+				enableAnimation:
+					! typing &&
+					getGlobalBlockCount() <= BLOCK_ANIMATION_THRESHOLD,
+				isSubtreeDisabled: isBlockSubtreeDisabled( clientId ),
+				isOutlineEnabled: outlineMode,
+				hasOverlay: __unstableHasActiveBlockOverlayActive( clientId ),
+				initialPosition:
+					_isSelected && __unstableGetEditorMode() === 'edit'
+						? getSelectedBlocksInitialCaretPosition()
+						: undefined,
+				isHighlighted: isBlockHighlighted( clientId ),
+				isMultiSelected,
+				isPartiallySelected:
+					isMultiSelected &&
+					! __unstableIsFullySelected() &&
+					! __unstableSelectionHasUnmergeableBlock(),
+				isReusable: isReusableBlock( blockType ),
+				isDragging: isBlockBeingDragged( clientId ),
+				hasChildSelected: isAncestorOfSelectedBlock,
+				removeOutline: _isSelected && outlineMode && typing,
+				isBlockMovingMode: !! movingClientId,
+				canInsertMovingBlock:
+					movingClientId &&
+					canInsertBlockType(
+						getBlockName( movingClientId ),
+						getBlockRootClientId( clientId )
+					),
+				isEditingDisabled:
+					getBlockEditingMode( clientId ) === 'disabled',
+				className: hasLightBlockWrapper
+					? attributes.className
+					: undefined,
+				defaultClassName: hasLightBlockWrapper
+					? getBlockDefaultClassName( blockName )
+					: undefined,
+			};
+		},
+		[ clientId, rootClientId ]
+	);
+
+	const {
+		mode,
+		isSelectionEnabled,
+		isLocked,
+		canRemove,
+		canMove,
+		block,
+		name,
+		attributes,
+		isValid,
+		isSelected,
+		themeSupportsLayout,
+		isTemporarilyEditingAsBlocks,
+		blockEditingMode,
+		mayDisplayControls,
+		mayDisplayParentControls,
+		index,
+		blockApiVersion,
+		blockTitle,
+		isPartOfSelection,
+		adjustScrolling,
+		enableAnimation,
+		isSubtreeDisabled,
+		isOutlineEnabled,
+		hasOverlay,
+		initialPosition,
+		isHighlighted,
+		isMultiSelected,
+		isPartiallySelected,
+		isReusable,
+		isDragging,
+		hasChildSelected,
+		removeOutline,
+		isBlockMovingMode,
+		canInsertMovingBlock,
+		isEditingDisabled,
+		className,
+		defaultClassName,
+	} = selectedProps;
+
+	// Block is sometimes not mounted at the right time, causing it be
+	// undefined see issue for more info
+	// https://github.com/WordPress/gutenberg/issues/17013
+	if ( ! selectedProps ) {
+		return null;
+	}
+
+	const privateContext = {
+		clientId,
+		className,
+		index,
+		mode,
+		name,
+		blockApiVersion,
+		blockTitle,
+		isSelected,
+		isPartOfSelection,
+		adjustScrolling,
+		enableAnimation,
+		isSubtreeDisabled,
+		isOutlineEnabled,
+		hasOverlay,
+		initialPosition,
+		blockEditingMode,
+		isHighlighted,
+		isMultiSelected,
+		isPartiallySelected,
+		isReusable,
+		isDragging,
+		hasChildSelected,
+		removeOutline,
+		isBlockMovingMode,
+		canInsertMovingBlock,
+		isEditingDisabled,
+		isTemporarilyEditingAsBlocks,
+		defaultClassName,
+		mayDisplayControls,
+		mayDisplayParentControls,
+		themeSupportsLayout,
+	};
+
+	// Here we separate between the props passed to BlockListBlock and any other
+	// information we selected for internal use. BlockListBlock is a filtered
+	// component and thus ALL the props are PUBLIC API.
+
+	// Note that the context value doesn't have to be memoized in this case
+	// because when it changes, this component will be re-rendered anyway, and
+	// none of the consumers (BlockListBlock and useBlockProps) are memoized or
+	// "pure". This is different from the public BlockEditContext, where
+	// consumers might be memoized or "pure".
+	return (
+		<PrivateBlockContext.Provider value={ privateContext }>
+			<BlockListBlock
+				{ ...props }
+				// WARNING: all the following props are public API (through the
+				// editor.BlockListBlock filter) and normally nothing new should
+				// be added to it.
+				{ ...{
+					mode,
+					isSelectionEnabled,
+					isLocked,
+					canRemove,
+					canMove,
+					// Users of the editor.BlockListBlock filter used to be able
+					// to access the block prop. Ideally these blocks would rely
+					// on the clientId prop only. This is kept for backward
+					// compatibility reasons.
+					block,
+					name,
+					attributes,
+					isValid,
+					isSelected,
+				} }
+			/>
+		</PrivateBlockContext.Provider>
+	);
+}
+
+export default pure( BlockListBlockProvider );
diff --git a/packages/block-editor/src/components/block-list/block-list-block-context.js b/packages/block-editor/src/components/block-list/private-block-context.js
similarity index 59%
rename from packages/block-editor/src/components/block-list/block-list-block-context.js
rename to packages/block-editor/src/components/block-list/private-block-context.js
index 6fa09c6969ec59..14981058965019 100644
--- a/packages/block-editor/src/components/block-list/block-list-block-context.js
+++ b/packages/block-editor/src/components/block-list/private-block-context.js
@@ -3,4 +3,4 @@
  */
 import { createContext } from '@wordpress/element';
 
-export const BlockListBlockContext = createContext( null );
+export const PrivateBlockContext = createContext( null );
diff --git a/packages/block-editor/src/components/block-list/use-block-props/index.js b/packages/block-editor/src/components/block-list/use-block-props/index.js
index 593beafa06d83f..fea20506c28a1f 100644
--- a/packages/block-editor/src/components/block-list/use-block-props/index.js
+++ b/packages/block-editor/src/components/block-list/use-block-props/index.js
@@ -8,22 +8,15 @@ import classnames from 'classnames';
  */
 import { useContext } from '@wordpress/element';
 import { __, sprintf } from '@wordpress/i18n';
-import {
-	__unstableGetBlockProps as getBlockProps,
-	getBlockType,
-	isReusableBlock,
-	getBlockDefaultClassName,
-	store as blocksStore,
-} from '@wordpress/blocks';
+import { __unstableGetBlockProps as getBlockProps } from '@wordpress/blocks';
 import { useMergeRefs, useDisabled } from '@wordpress/compose';
-import { useSelect } from '@wordpress/data';
 import warning from '@wordpress/warning';
 
 /**
  * Internal dependencies
  */
 import useMovingAnimation from '../../use-moving-animation';
-import { BlockListBlockContext } from '../block-list-block-context';
+import { PrivateBlockContext } from '../private-block-context';
 import { useFocusFirstElement } from './use-focus-first-element';
 import { useIsHovered } from './use-is-hovered';
 import { useBlockEditContext } from '../../block-edit/context';
@@ -32,14 +25,6 @@ import { useEventHandlers } from './use-selected-block-event-handlers';
 import { useNavModeExit } from './use-nav-mode-exit';
 import { useBlockRefProvider } from './use-block-refs';
 import { useIntersectionObserver } from './use-intersection-observer';
-import { store as blockEditorStore } from '../../../store';
-import { unlock } from '../../../lock-unlock';
-
-/**
- * If the block count exceeds the threshold, we disable the reordering animation
- * to avoid laginess.
- */
-const BLOCK_ANIMATION_THRESHOLD = 200;
 
 /**
  * This hook is used to lightly mark an element as a block element. The element
@@ -89,8 +74,6 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
 		className,
 		wrapperProps = {},
 		isAligned,
-	} = useContext( BlockListBlockContext );
-	const {
 		index,
 		mode,
 		name,
@@ -104,104 +87,20 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
 		isOutlineEnabled,
 		hasOverlay,
 		initialPosition,
-		classNames,
-	} = useSelect(
-		( select ) => {
-			const {
-				getBlockAttributes,
-				getBlockIndex,
-				getBlockMode,
-				getBlockName,
-				isTyping,
-				getGlobalBlockCount,
-				isBlockSelected,
-				isBlockMultiSelected,
-				isAncestorMultiSelected,
-				isFirstMultiSelectedBlock,
-				isBlockSubtreeDisabled,
-				getSettings,
-				isBlockHighlighted,
-				__unstableIsFullySelected,
-				__unstableSelectionHasUnmergeableBlock,
-				isBlockBeingDragged,
-				hasSelectedInnerBlock,
-				hasBlockMovingClientId,
-				canInsertBlockType,
-				getBlockRootClientId,
-				__unstableHasActiveBlockOverlayActive,
-				__unstableGetEditorMode,
-				getSelectedBlocksInitialCaretPosition,
-			} = unlock( select( blockEditorStore ) );
-			const { getActiveBlockVariation } = select( blocksStore );
-			const _isSelected = isBlockSelected( clientId );
-			const isPartOfMultiSelection =
-				isBlockMultiSelected( clientId ) ||
-				isAncestorMultiSelected( clientId );
-			const blockName = getBlockName( clientId );
-			const blockType = getBlockType( blockName );
-			const attributes = getBlockAttributes( clientId );
-			const match = getActiveBlockVariation( blockName, attributes );
-			const { outlineMode } = getSettings();
-			const isMultiSelected = isBlockMultiSelected( clientId );
-			const checkDeep = true;
-			const isAncestorOfSelectedBlock = hasSelectedInnerBlock(
-				clientId,
-				checkDeep
-			);
-			const typing = isTyping();
-			const hasLightBlockWrapper = blockType?.apiVersion > 1;
-			const movingClientId = hasBlockMovingClientId();
-
-			return {
-				index: getBlockIndex( clientId ),
-				mode: getBlockMode( clientId ),
-				name: blockName,
-				blockApiVersion: blockType?.apiVersion || 1,
-				blockTitle: match?.title || blockType?.title,
-				isSelected: _isSelected,
-				isPartOfSelection: _isSelected || isPartOfMultiSelection,
-				adjustScrolling:
-					_isSelected || isFirstMultiSelectedBlock( clientId ),
-				enableAnimation:
-					! typing &&
-					getGlobalBlockCount() <= BLOCK_ANIMATION_THRESHOLD,
-				isSubtreeDisabled: isBlockSubtreeDisabled( clientId ),
-				isOutlineEnabled: outlineMode,
-				hasOverlay: __unstableHasActiveBlockOverlayActive( clientId ),
-				initialPosition:
-					_isSelected && __unstableGetEditorMode() === 'edit'
-						? getSelectedBlocksInitialCaretPosition()
-						: undefined,
-				classNames: classnames(
-					{
-						'is-selected': _isSelected,
-						'is-highlighted': isBlockHighlighted( clientId ),
-						'is-multi-selected': isMultiSelected,
-						'is-partially-selected':
-							isMultiSelected &&
-							! __unstableIsFullySelected() &&
-							! __unstableSelectionHasUnmergeableBlock(),
-						'is-reusable': isReusableBlock( blockType ),
-						'is-dragging': isBlockBeingDragged( clientId ),
-						'has-child-selected': isAncestorOfSelectedBlock,
-						'remove-outline': _isSelected && outlineMode && typing,
-						'is-block-moving-mode': !! movingClientId,
-						'can-insert-moving-block':
-							movingClientId &&
-							canInsertBlockType(
-								getBlockName( movingClientId ),
-								getBlockRootClientId( clientId )
-							),
-					},
-					hasLightBlockWrapper ? attributes.className : undefined,
-					hasLightBlockWrapper
-						? getBlockDefaultClassName( blockName )
-						: undefined
-				),
-			};
-		},
-		[ clientId ]
-	);
+		blockEditingMode,
+		isHighlighted,
+		isMultiSelected,
+		isPartiallySelected,
+		isReusable,
+		isDragging,
+		hasChildSelected,
+		removeOutline,
+		isBlockMovingMode,
+		canInsertMovingBlock,
+		isEditingDisabled,
+		isTemporarilyEditingAsBlocks,
+		defaultClassName,
+	} = useContext( PrivateBlockContext );
 
 	// translators: %s: Type of block (i.e. Text, Image etc)
 	const blockLabel = sprintf( __( 'Block: %s' ), blockTitle );
@@ -233,7 +132,7 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
 	}
 
 	return {
-		tabIndex: 0,
+		tabIndex: blockEditingMode === 'disabled' ? -1 : 0,
 		...wrapperProps,
 		...props,
 		ref: mergedRefs,
@@ -250,11 +149,24 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
 				// The wp-block className is important for editor styles.
 				'wp-block': ! isAligned,
 				'has-block-overlay': hasOverlay,
+				'is-selected': isSelected,
+				'is-highlighted': isHighlighted,
+				'is-multi-selected': isMultiSelected,
+				'is-partially-selected': isPartiallySelected,
+				'is-reusable': isReusable,
+				'is-dragging': isDragging,
+				'has-child-selected': hasChildSelected,
+				'remove-outline': removeOutline,
+				'is-block-moving-mode': isBlockMovingMode,
+				'can-insert-moving-block': canInsertMovingBlock,
+				'is-editing-disabled': isEditingDisabled,
+				'is-content-locked-temporarily-editing-as-blocks':
+					isTemporarilyEditingAsBlocks,
 			},
 			className,
 			props.className,
 			wrapperProps.className,
-			classNames
+			defaultClassName
 		),
 		style: { ...wrapperProps.style, ...props.style },
 	};