Skip to content

Commit 2359da7

Browse files
ellatrixclaude
andcommitted
Render the selected static inner block synchronously
Blocks with `innerContent` support portal their inner blocks directly via `InnerContent`, bypassing the per-block `AsyncModeProvider` the block list wraps around each child. As a result the selected inner block runs in async mode, so its `useSelect` subscriptions (including the rich text selection) invalidate on a deferred schedule. While typing, a content-driven re-render can then read a stale selection offset before the deferred invalidation lands, corrupting the caret. Wrap each portalled inner block in an `AsyncModeProvider` keyed on its selection, matching the block list, so the selected block renders synchronously and its selection stays current. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent af53431 commit 2359da7

1 file changed

Lines changed: 18 additions & 7 deletions

File tree

  • packages/block-editor/src/components/inner-content

packages/block-editor/src/components/inner-content/index.js

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
useRef,
99
useState,
1010
} from '@wordpress/element';
11-
import { useSelect } from '@wordpress/data';
11+
import { AsyncModeProvider, useSelect } from '@wordpress/data';
1212
import { safeHTML } from '@wordpress/dom';
1313

1414
/**
@@ -43,12 +43,14 @@ const LAYOUT = { type: 'default', alignments: [] };
4343
* should be rendered.
4444
*/
4545
export default function InnerContent( { clientId } ) {
46-
const { innerContent, order } = useSelect(
46+
const { innerContent, order, selectedClientIds } = useSelect(
4747
( select ) => {
48-
const { getBlock, getBlockOrder } = select( blockEditorStore );
48+
const { getBlock, getBlockOrder, getSelectedBlockClientIds } =
49+
select( blockEditorStore );
4950
return {
5051
innerContent: getBlock( clientId )?.innerContent,
5152
order: getBlockOrder( clientId ),
53+
selectedClientIds: getSelectedBlockClientIds(),
5254
};
5355
},
5456
[ clientId ]
@@ -94,10 +96,19 @@ export default function InnerContent( { clientId } ) {
9496
{ order.map( ( childClientId, index ) =>
9597
slots[ index ]
9698
? createPortal(
97-
<BlockListBlock
98-
rootClientId={ clientId }
99-
clientId={ childClientId }
100-
/>,
99+
// Render the selected block synchronously, as the block list does.
100+
<AsyncModeProvider
101+
value={
102+
! selectedClientIds.includes(
103+
childClientId
104+
)
105+
}
106+
>
107+
<BlockListBlock
108+
rootClientId={ clientId }
109+
clientId={ childClientId }
110+
/>
111+
</AsyncModeProvider>,
101112
slots[ index ],
102113
childClientId
103114
)

0 commit comments

Comments
 (0)