Skip to content

Commit

Permalink
Merge pull request #49 from nytimes/flush-sync-composing
Browse files Browse the repository at this point in the history
Modify useEditorView to flushSync all dispatched transactions
  • Loading branch information
ilyaGurevich authored Sep 11, 2023
2 parents c1b8f3f + fa161fb commit 64dd486
Show file tree
Hide file tree
Showing 2 changed files with 9 additions and 20 deletions.
2 changes: 2 additions & 0 deletions .yarn/versions/290b480a.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
releases:
"@nytimes/react-prosemirror": minor
27 changes: 7 additions & 20 deletions src/hooks/useEditorView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import type { EditorState, Transaction } from "prosemirror-state";
import { EditorView } from "prosemirror-view";
import type { DirectEditorProps } from "prosemirror-view";
import { useLayoutEffect, useState } from "react";
import { unstable_batchedUpdates as batch } from "react-dom";
import { flushSync } from "react-dom";

import { useForceUpdate } from "./useForceUpdate.js";

function withBatchedUpdates<This, T extends unknown[]>(
function withFlushedUpdates<This, T extends unknown[]>(
fn: (this: This, ...args: T) => void
): (...args: T) => void {
return function (this: This, ...args: T) {
batch(() => {
flushSync(() => {
fn.call(this, ...args);
});
};
Expand All @@ -30,19 +30,7 @@ type EditorStateProps =

export type EditorProps = Omit<DirectEditorProps, "state"> & EditorStateProps;

/**
* Enhances editor props so transactions dispatch in a batched update.
*
* It is important that changes to the editor get batched by React so that any
* components that dispatch transactions in effects do so after rendering with
* state changes from any previous transaction, so that they may use the latest
* state and not trigger nested transactions.
*
* TODO(OK-4006): We can remove this helper and pass the direct editor props to
* the Editor View unmodified after we upgrade to React 18, which batches every
* update by default.
*/
function withBatchedDispatch(
function withFlushedDispatch(
props: EditorProps,
forceUpdate: () => void
): EditorProps & {
Expand All @@ -55,10 +43,10 @@ function withBatchedDispatch(
this: EditorView,
tr: Transaction
) {
const batchedDispatchTransaction = withBatchedUpdates(
const flushedDispatch = withFlushedUpdates(
props.dispatchTransaction ?? defaultDispatchTransaction
);
batchedDispatchTransaction.call(this, tr);
flushedDispatch.call(this, tr);
if (!("state" in props)) forceUpdate();
},
},
Expand All @@ -79,10 +67,9 @@ export function useEditorView<T extends HTMLElement = HTMLElement>(
props: EditorProps
): EditorView | null {
const [view, setView] = useState<EditorView | null>(null);

const forceUpdate = useForceUpdate();

const editorProps = withBatchedDispatch(props, forceUpdate);
const editorProps = withFlushedDispatch(props, forceUpdate);

const stateProp = "state" in editorProps ? editorProps.state : undefined;

Expand Down

0 comments on commit 64dd486

Please sign in to comment.