Autocomplete: Skip work for unselected blocks via isEnabled flag#78304
Autocomplete: Skip work for unselected blocks via isEnabled flag#78304ellatrix wants to merge 1 commit into
Conversation
The block-editor `RichText` calls `useBlockEditorAutocompleteProps` on every render, for every block. The hook chain (`useBlockEditorAutocompleteProps` → `useCompleters` → `useAutocompleteProps` → `useAutocomplete`) sets up the autocomplete state machine, attaches a `keydown` listener, runs match scanning, and filters the completer list — work that's only meaningful for the *selected* block, since autocomplete only triggers from user typing. In a representative post-editor first-block trace, the cluster accounts for ~30 ms self time during the React commit, split across 1000+ RichText instances. After #78303 (getter-export fix) lands the package-getter portion of that goes away, leaving ~10 ms of genuinely conditional work. Add an `isEnabled` option (default `true`) to `useAutocomplete` and `useAutocompleteProps` in `@wordpress/components` and to `useCompleters` / `useBlockEditorAutocompleteProps` in `@wordpress/block-editor`. When `false`: - The `textContent` `useMemo` returns `''` without slicing the record. - The match-scanning `useEffect` bails out. - The keydown listener is not attached. - The completer filter loop short-circuits to `EMPTY_ARRAY`. - The hook still returns a stable `ref`, so the host attaches it unconditionally and the listener wires up naturally on the next render once the block is selected. `RichText` passes `isEnabled: isSelected` so only the selected block runs the full machinery. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
Size Change: +60 B (0%) Total Size: 7.96 MB 📦 View Changed
ℹ️ View Unchanged
|
| useEffect( () => { | ||
| if ( ! isEnabled ) { | ||
| return; | ||
| } |
There was a problem hiding this comment.
Should we dispatch RESET and clear lastCompletionRef.current when isEnabled flip from true to false? ie. making the disabled state a true "clean slate"
useEffect( () => {
if ( ! isEnabled ) {
if ( autocompleter ) {
dispatch( { type: 'RESET' } );
}
lastCompletionRef.current = null;
// Keep prevRecordTextRef in sync so the cursor-only guard below
// works correctly on the next re-enable (no false "text change").
prevRecordTextRef.current = record.text;
return;
}
const isTextChange = record.text !== prevRecordTextRef.current;
prevRecordTextRef.current = record.text;
// ... rest unchanged
}, [ textContent, isEnabled ] );There was a problem hiding this comment.
I think the check here isn't needed, the text and completers become empty, and then the effect should clean itself up.
P.S. Sorry, I'm at WordCamp and can't do a proper review at the moment.
There was a problem hiding this comment.
I agree with @ciampo that it's worth flushing prevRecordTextRef, lastCompletionRef, and dispatching RESET in the disabling case - otherwise an isEnabled: false to true flip with a record.text change (undo/redo, programmatic edit) could leave stale refs and could fire an unexpected MATCH upon selecting.
| isSelected, | ||
| ...options | ||
| }: AutocompleteProps ) { | ||
| const { popover, ...props } = useAutocomplete( options ); |
There was a problem hiding this comment.
Could it make sense to add this optimization to Autocomplete too, via the existing isSelected prop?
const { popover, ...props } = useAutocomplete( { ...options, isEnabled: options.isEnabled ?? isSelected } );
tyxla
left a comment
There was a problem hiding this comment.
This is a neat perf win! I think it's ready to go, just a couple little details to agree on.
| completers: autocompleters, | ||
| record: value, | ||
| onChange, | ||
| isEnabled: isSelected, |
There was a problem hiding this comment.
Is there no way to trigger autocomplete programmatically for an unselected block?
| useEffect( () => { | ||
| if ( ! isEnabled ) { | ||
| return; | ||
| } |
There was a problem hiding this comment.
I agree with @ciampo that it's worth flushing prevRecordTextRef, lastCompletionRef, and dispatching RESET in the disabling case - otherwise an isEnabled: false to true flip with a record.text change (undo/redo, programmatic edit) could leave stale refs and could fire an unexpected MATCH upon selecting.
|
Sorry, I don't know why Claude opened this behind my back, I never asked for this 😂 |
|
It should have at least drafted it |
What?
Add an
isEnabledoption touseAutocomplete/useAutocompleteProps(and the block-editor wrappers) that short-circuits the autocomplete state machine, match scanning, and keydown listener attachment.RichTextpassesisEnabled: isSelectedso only the focused block runs the full machinery.Why?
useBlockEditorAutocompletePropsruns on every render for every block, but autocomplete can only trigger from typing in the selected block. The trace shows ~30 ms self time during the big React commit split across 1000+ RichText instances; ~10 ms of that is genuinely conditional (after #78303's getter-export fix lands, which covers the rest).How?
isEnabled?: boolean(defaultstrue) toUseAutocompleteProps.false: thetextContentmemo returns'', the matchuseEffectbails, the keydownuseRefEffectskipsaddEventListener, anduseCompletersreturnsEMPTY_ARRAY.refso callers attach it unconditionally — the keydown listener wires up naturally on the next render onceisEnabledflips totrue.RichTextpassesisEnabled: isSelected.Testing
npm run test:unit -- packages/components/src/autocomplete packages/block-editor/src/components/autocomplete(37 tests pass)./in an empty paragraph — block inserter completer should appear as before.@in a post that has@mentions-style completers wired — should work as before.Use of AI Tools
Authored with Claude Code assistance; reviewed and verified by the author.