[Bug]: Node Views Causes Extra Re-draws When EditorContent Mounts

by ADMIN 66 views

Introduction

When using React with extensions that include node views in Prosemirror, the editor is initially re-drawn several times. This can cause performance issues, particularly when prosemirror-view resets the text selection. In this article, we will explore the issue, provide a code example, and discuss the expected behavior.

Affected Packages and Version

Affected Packages

  • core
  • react

Version(s)

  • 2.11.7

Bug Description

When using React with extensions that include node views, the editor is initially re-drawn several times. prosemirror-view's internal updateStateInner method is called three times, and the document is re-drawn because the node views are changing. This can be seen in the prosemirror-view source code, specifically in the updateStateInner method.

// https://github.com/ProseMirror/prosemirror-view/blob/4867a987d4e54768492dc167bf3a8340e2dd91f5/src/index.ts#L166
updateStateInner(state, dispatch, view) {
  // ...
  if (view.state.docChanged) {
    view.updateState(state);
  }
  // ...
}

As you can see, the updateStateInner method is called three times, and the document is re-drawn each time.

Browser Used

  • Chrome

Code Example URL

Expected Behavior

Draw the document only once on mount.

Additional Context (Optional)

I think that updateState is called three times:

  • First when the editor is created: The editor only contains the node views created directly with a Prosemirror plugin via addProseMirrorPlugins. The document is drawn.
  • The editor then calls createNodeViews: Which again calls updateState. The editor now contains the node views created via addNodeView. The document is redrawn.
  • Finally, the EditorContent component calls createNodeViews again: But the node views are erroneously considered different, so the document is redrawn a third time.
// https://github.com/ueberdosis/tiptap/blob/6ceff32242dc815acb121717752467f205d52e42/packages/core/src/Editor.ts#L382
editor.create();

// https://github.com/ueberdosis/tiptap/blob/6ceff32242dc815acb121717752467f205d52e42/packages/core/src/Editor.ts#L384
editor.createNodeViews();

// https://github.com/ueberdosis/tiptap/blob/6ceff32242dc815acb121717752467f205d52e42/packages/react/src/EditorContent.tsx#L161
EditorContent.createNodeViews();

Workaround

I can avoid this issue by avoiding addNodeView and only using addProsemirrorPlugins.

Dependency Updates

  • [x] Yes, I've updated my dependencies.

Conclusion

In conclusion, the issue of node views causing extra re-draws when EditorContent mounts is a known problem in Prosemirror. By avoiding addNodeView and only using addProsemirrorPlugins, we can avoid this issue. However, this is just a workaround, and a proper fix is needed to resolve this issue once and for all.

Future Work

In the future, we can work on resolving this issue by:

  • Investigating why updateState is called three times
  • Fixing the issue with createNodeViews being called multiple times
  • Improving the performance of prosemirror-view to reduce the number of re-draws

Q: What is the issue with node views causing extra re-draws when EditorContent mounts?

A: The issue is that prosemirror-view's internal updateStateInner method is called three times, and the document is re-drawn because the node views are changing. This can cause performance issues, particularly when prosemirror-view resets the text selection.

Q: Why is updateState called three times?

A: updateState is called three times because of the following sequence of events:

  • First when the editor is created: The editor only contains the node views created directly with a Prosemirror plugin via addProseMirrorPlugins. The document is drawn.
  • The editor then calls createNodeViews: Which again calls updateState. The editor now contains the node views created via addNodeView. The document is redrawn.
  • Finally, the EditorContent component calls createNodeViews again: But the node views are erroneously considered different, so the document is redrawn a third time.

Q: How can I avoid this issue?

A: You can avoid this issue by avoiding addNodeView and only using addProsemirrorPlugins.

Q: What are the performance implications of this issue?

A: The performance implications of this issue are that the editor is re-drawn multiple times, which can cause performance issues, particularly when prosemirror-view resets the text selection.

Q: How can I improve the performance of my editor?

A: To improve the performance of your editor, you can:

  • Avoid using addNodeView and only use addProsemirrorPlugins
  • Optimize your node views to reduce the number of re-draws
  • Use a more efficient rendering engine, such as prosemirror-view's updateStateInner method

Q: Is this issue specific to Prosemirror?

A: No, this issue is not specific to Prosemirror. It can occur in any rich text editor that uses a similar architecture.

Q: How can I report this issue to the Prosemirror team?

A: You can report this issue to the Prosemirror team by opening an issue on the Prosemirror GitHub repository.

Q: What is the expected behavior of the editor when EditorContent mounts?

A: The expected behavior of the editor when EditorContent mounts is to draw the document only once.

Q: How can I verify that the issue is fixed?

A: To verify that the issue is fixed, you can:

  • Check that updateState is only called once
  • Verify that the document is only drawn once
  • Test the editor's performance to ensure that it is improved

By following these steps, you can help to resolve this issue and improve the performance of your editor.