Skip to content

fix: keep React updates from walking the whole editor while typing - #694

Merged
Azganoth merged 1 commit into
mainfrom
bug/react-commit-selection-walk
Oct 12, 2026
Merged

Azganoth merged 1 commit into
mainfrom
bug/react-commit-selection-walk

Conversation

@Azganoth

Copy link
Copy Markdown
Owner

Summary

Before every commit, React DOM saves the selection of the focused element when its contentEditable property reads "true", counting text offsets over every node beneath it. While the editor had focus, each React commit therefore walked the whole document. In a release build of main, these commits happen while the editor has focus:

  • StatusBar on every keystroke, inside the key's own task, for the word and character counts and the block path.
  • HeadingOutline about one frame later on every keystroke when a heading follows the caret, since the headings' positions shift.
  • Shell on the first edit and when the command state changes.
  • Base UI ScrollArea when the view scrolls, including wheel scrolling.

What changes:

  • A new editor plugin gives the editor's root element an own contentEditable getter that reports "inherit" where the element's value is "true", so React no longer treats the editor as selection-capable and skips the walk. The contenteditable attribute, isContentEditable, and editing are unchanged, and the plugin removes the property when the editor is destroyed.
  • Nothing is deferred: the status bar, outline, dirty state, and other React-rendered state commit exactly as before.
  • CHANGELOG.md records the faster typing.

Deferring the status bar and outline commits until typing pauses was rejected: it would delay what the user sees, and each deferred commit would still walk the document.

Related Issue

Closes #685

Verification

  • A focused test focuses the editor, places the caret, and triggers a React commit while counting reads of the editor's text nodes. It asserts no reads, then deletes the plugin's property and asserts the walk returns, so the test fails if React changes how it decides to save a focused element's selection. Other tests cover the attribute staying "true", a read-only editor reporting "false", and removal on destroy.
  • pnpm check:frontend passed.

Manually verified in release builds on Windows 11, driven over the WebView2 DevTools protocol with the 1 MB code and table fixtures from #680:

  1. A temporary React DevTools hook on main at 9aa80f9 recorded the commits listed in the Summary, every one with the editor focused.

  2. This change built on 9aa80f9 against the same build with only the plugin's registration removed, median of 5 keystrokes, script time and keystroke-to-paint in ms:

    Target Script, before → after Keystroke to paint, before → after
    Code fixture, paragraph 56 → 27 168 → 152
    Code fixture, code block 164 → 103 296 → 224
    Tables fixture, paragraph 103 → 69 192 → 152
    Tables fixture, cell 111 → 67 192 → 144
  3. In CPU profiles of one keystroke per target, React's time fell from 49–87 ms, 44–74 ms of it in commitBeforeMutationEffects, to 0–2.4 ms, and that function no longer appeared.

  4. On the branch, the status bar's character count rose by one with every keystroke.

Not verified: scrolling times, and timings after rebasing onto #693.

Notes

The fix depends on React DOM's hasSelectionCapabilities reading elem.contentEditable === "true" (React DOM 19.3.0). If React changes that check, the walk returns without breaking behavior, and the focused test fails. When a commit moves focus out of the editor, React still refocuses it but no longer sets the caret itself; the browser keeps the editor's own selection.

@Azganoth Azganoth added the Bug Something isn't working label Oct 12, 2026
@Azganoth Azganoth self-assigned this Oct 12, 2026
@Azganoth
Azganoth merged commit 5e6736f into main Oct 12, 2026
3 checks passed
@Azganoth
Azganoth deleted the bug/react-commit-selection-walk branch October 12, 2026 23:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something isn't working

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Every React commit while typing walks the whole editor DOM

1 participant