Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Strict mode and memoize node view components #142

Open
wants to merge 24 commits into
base: react-editor-view
Choose a base branch
from

Conversation

smoores-dev
Copy link
Collaborator

@smoores-dev smoores-dev commented Oct 9, 2024

The primary goal of these changes is to allow a single node view component to re-render in isolation without affecting the correctness of the view descriptor tree.

Previously, it was required that the entire ProseMirror component tree re-render on every document update in order to maintain the view descriptor tree. The trees are built from the leaves up in useLayoutEffects, and the tree was built from scratch on each render cycle.

Now, the view descriptors are maintained in refs across renders, and mutated as needed, rather than built from scratch. This fixes an issue with strict mode (#128), since strict mode will render each component twice when it's first mounted. It also allows us to wrap each view component in React.memo, which quite dramatically improves the performance of the editor on very long documents. As a case study, working on an editor with the entire contents of Moby Dick in it:

Before these changes, each update took over 100ms.

After these changes, an update at the very end of the document takes less than 10ms. An update at the very beginning of the document takes less than 30ms.

Updates at the beginning of the document still take longer than updates at the end. This is because the new ChildElement (not a great name, will workshop) needs to re-render whenever its node's offset into its parent has changed, which happens each time the document is modified before its node. It seems there ought to be a way to avoid this, since the offset is only necessary to update a ref used for the getPos prop, but I haven't figured out a way to do it cleanly yet.

On that note, in order to migrate from the pos prop to the new getPos prop, and in particular to keep the getPos reference stable across renders, I had to introduce a few new layers of components in ChildNodeViews. They just encapsulate logic that was in loops before, and add some useRef calls to maintain stable refs to getPos functions.

For some reason the tests are timing out in CI, even though they're passing locally. I'll try to see if I can figure out what's going on there.

@smoores-dev smoores-dev marked this pull request as ready for review October 9, 2024 17:32
@smoores-dev smoores-dev requested a review from a team as a code owner October 9, 2024 17:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant