diff --git a/packages/lexical/src/LexicalUtils.ts b/packages/lexical/src/LexicalUtils.ts index 9039a186c50..b1a409a9f36 100644 --- a/packages/lexical/src/LexicalUtils.ts +++ b/packages/lexical/src/LexicalUtils.ts @@ -1314,6 +1314,19 @@ export function $addUpdateTag(tag: string): void { editor._updateTags.add(tag); } +/** + * Add a function to run after the current update. This will run after any + * `onUpdate` function already supplied to `editor.update()`, as well as any + * functions added with previous calls to `$onUpdate`. + * + * @param updateFn The function to run after the current update. + */ +export function $onUpdate(updateFn: () => void): void { + errorOnReadOnly(); + const editor = getActiveEditor(); + editor._deferred.push(updateFn); +} + export function $maybeMoveChildrenSelectionToParent( parentNode: LexicalNode, ): BaseSelection | null { diff --git a/packages/lexical/src/__tests__/unit/LexicalUtils.test.ts b/packages/lexical/src/__tests__/unit/LexicalUtils.test.ts index 2a497860b07..e360eac2486 100644 --- a/packages/lexical/src/__tests__/unit/LexicalUtils.test.ts +++ b/packages/lexical/src/__tests__/unit/LexicalUtils.test.ts @@ -23,6 +23,7 @@ import { } from 'lexical'; import { + $onUpdate, emptyFunction, generateRandomKey, getCachedTypeToNodeMap, @@ -242,6 +243,43 @@ describe('LexicalUtils tests', () => { }); }); + describe('$onUpdate', () => { + test('added fn runs after update, original onUpdate, and prior calls to $onUpdate', () => { + const {editor} = testEnv; + const runs: string[] = []; + + editor.update( + () => { + $getRoot().append( + $createParagraphNode().append($createTextNode('foo')), + ); + $onUpdate(() => { + runs.push('second'); + }); + $onUpdate(() => { + runs.push('third'); + }); + }, + { + onUpdate: () => { + runs.push('first'); + }, + }, + ); + + // Flush pending updates + editor.read(() => {}); + + expect(runs).toEqual(['first', 'second', 'third']); + }); + + test('adding fn throws outside update', () => { + expect(() => { + $onUpdate(() => {}); + }).toThrow(); + }); + }); + test('getCachedTypeToNodeMap', async () => { const {editor} = testEnv; const paragraphKeys: string[] = []; diff --git a/packages/lexical/src/index.ts b/packages/lexical/src/index.ts index 538440b8195..0bc18239b37 100644 --- a/packages/lexical/src/index.ts +++ b/packages/lexical/src/index.ts @@ -172,6 +172,7 @@ export { $isRootOrShadowRoot, $isTokenOrSegmented, $nodesOfType, + $onUpdate, $selectAll, $setCompositionKey, $setSelection,