Skip to content

[lexical] Bug Fix: Defer onUpdate callbacks during nested commits#8672

Open
Rohithmatham12 wants to merge 1 commit into
facebook:mainfrom
Rohithmatham12:robust-focus-deferred-updates
Open

[lexical] Bug Fix: Defer onUpdate callbacks during nested commits#8672
Rohithmatham12 wants to merge 1 commit into
facebook:mainfrom
Rohithmatham12:robust-focus-deferred-updates

Conversation

@Rohithmatham12

@Rohithmatham12 Rohithmatham12 commented Jun 10, 2026

Copy link
Copy Markdown

Description

When setEditorState() is called from inside an active editor.update(), it can force a commit before the outer update callback has returned. That nested commit currently drains editor._deferred, so the outer update's onUpdate callback can run before the update that scheduled it has completed.

This keeps deferred callbacks queued while a commit is forced during an existing update. The outer update then drains them after its update function returns, preserving the expected $onUpdate timing. The existing no-pending-state fallback now also avoids draining callbacks while an update is active.

Closes #8359

Test plan

Before

Calling setEditorState() from inside an active editor.update() could run deferred onUpdate callbacks before the outer update function returned.

After

Nested commits keep deferred callbacks queued until the outer update completes, so onUpdate timing stays consistent with the update lifecycle.

Automated tests:

  • /Users/rohithmatam/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin/node node_modules/vitest/vitest.mjs --project unit packages/lexical/src/__tests__/unit/LexicalEditor.test.tsx --run
  • /Users/rohithmatam/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin/node node_modules/vitest/vitest.mjs --project unit packages/lexical-react/src/__tests__/unit/LexicalExtensionComposer.test.tsx --run
  • /Users/rohithmatam/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin/node node_modules/prettier/bin/prettier.cjs --check packages/lexical/src/LexicalUpdates.ts packages/lexical/src/__tests__/unit/LexicalEditor.test.tsx
  • /Users/rohithmatam/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin/node node_modules/eslint/bin/eslint.js packages/lexical/src/LexicalUpdates.ts packages/lexical/src/__tests__/unit/LexicalEditor.test.tsx

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jun 10, 2026
@vercel

vercel Bot commented Jun 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
lexical Ready Ready Preview, Comment Jun 10, 2026 7:24pm
lexical-playground Ready Ready Preview, Comment Jun 10, 2026 7:24pm

Request Review

@potatowagon potatowagon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed by Navi (Tater Thoughts Bobblehead) on behalf of @potatowagon.

Assessment: LGTM

What this does: Fixes #8359 — when setEditorState() is called inside an active editor.update(), the nested commit was prematurely draining editor._deferred, causing onUpdate callbacks to fire before the outer update function returned. The fix gates triggerDeferredUpdateCallbacks on !previouslyUpdating, ensuring the outer update drains them after its updateFn completes.

What I checked:

  1. Logic correctness: The previouslyUpdating variable is already captured at the top of $commitPendingUpdates (line ~548) — reusing it here is clean. The no-pending-state early-return path (line ~547) now also checks !editor._updating, which is correct — if an update is active, deferred callbacks should wait.
  2. Edge cases: (a) Normal non-nested commits: previouslyUpdating is false → callbacks drain immediately as before. (b) Nested commit via setEditorState(): callbacks stay queued → outer update drains after returning. (c) Multiple nested commits: each defers; the final outer drain handles all accumulated callbacks.
  3. Test coverage: Comprehensive test verifies the exact ordering: update startupdate endafter update → (microtask) → onUpdate. Good use of Promise.resolve() to verify async drain.
  4. Regression risk: Low. The only behavioral change is callback timing for nested commits — callers should not depend on onUpdate firing synchronously mid-update. Aligns with documented $onUpdate semantics.
  5. www compat: No API surface changes. Internal timing fix only. Safe for downstream consumers.

CI: CLA ✅, Vercel ✅. Full test matrix not yet triggered (PR is <10min old) — recommend waiting for unit/browser/e2e green before merge.

Ready to approve once full CI passes.

@etrepum etrepum added the extended-tests Run extended e2e tests on a PR label Jun 10, 2026
@etrepum

etrepum commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Title and description doesn't match the pull request template

@Rohithmatham12 Rohithmatham12 changed the title Defer onUpdate callbacks during nested commits [lexical] Bug Fix: Defer onUpdate callbacks during nested commits Jun 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. extended-tests Run extended e2e tests on a PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Synchronous update re-entry issue with editor.focus

3 participants