-
Notifications
You must be signed in to change notification settings - Fork 19
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
Fix bug with infinite rendering loop which can sometimes happen with widget decorations #157
base: v1.x
Are you sure you want to change the base?
Conversation
Are there any cases where the |
I think if the component fails to mount due to error, this code will crash, so I'm not sure that we need to handle that scenario here (or if we did, we'd need different API's accounting for that possibility). I'm not aware of other situations which would cause the component to fail to eventually mount, but I'm still fuzzy enough on why the sequencing changes when a decoration widget is in play that I'm not sure that I can offer more confidence than that |
I'm taking a look to see what's going on here. In a quick debugging session I'm seeing that the paragraph does register and unregister its ref (twice, actually, but that may just be an effect of React's dev mode) before the new code you've added ever runs. But it does look like the code you've added does run before the textblock registers its ref. I'd like to understand why the order of things if different before we add defensive checks that may mask other issues down the line or lead to unexpected states. |
Oops, when I said |
We use a mount ref to give users control over the editing host element. The way |
With this change, the code you've added never gets called:
|
To summarize, I think this issue occurs when the decorations defined at the time that the editor mounts aren't keyed or otherwise memoized over state. If we want to account for this, I think the fix should be in |
The previous fix I was working on was indeed in calling code for the plugin this test case approximates, specifically to set a I'm OK to close this PR and fix upstream if we think the specific situation of |
By this do you mean in the application using react-prosemirror, or fixing something in prosemirror itself? |
I can fix in the application using (If this bug is possible with other non-widget decorations too, I might be more inclined to try to figure out a fix here) |
Happy to consider fixing things so that keyless widgets don't cause infinite loops at mount. I just don't think this PR is the fix I'd want to commit. |
I think we have a better understanding of the cause of the extra update cycle and why we end up doing a ProseMirror update at a moment in the React commit cycle that we didn't plan for. I would prefer to try to avoid that update or think a little harder about how to handle that update before committing a fix that could have unexpected effects in other scenarios. I'm concerned about keeping the mental model here clear. We expect that React will render and then commit, and as a result refs will exist before ProseMirror updates. Anything we do to accommodate other behavior is inviting trouble and confusion, I think. |
I'm taking a look now to see if I can get a better sense of why, specifically, we're getting into a loop here. One thing I found, incidentally, is that ProseMirror View considers two widgets equal if they:
Which means that in addition to fixing this with a key, you can also fix it by pulling out the toDOM function into module scope. Kind of funny, and doesn't fix the underlying problem (widgets should not cause a crash even if they get rebuilt on every update). Gonna keep digging! |
I'm reflecting on this idea again... |
Please have a look at #158 and close if you think that's a better way to go. After talking this through following our in person discussions today, I was able to figure out that pushing the node view portal management down into |
You could pursue this, for sure. I've thought about it, but haven't ever attempted it due to the way that ProseMirror mingles props and state. Tracking them separately would probably allow some optimizations in how ProseMirror decides what to recompute. In any case, I think #158 would make such a change into an optimization rather than a bug fix and I'm eager to hear your reaction. |
We've seen an issue where rendering a widget decoration into an empty ProseMirror node with an associated react-prosemirror NodeView causes an infinite loop: the NodeView's
update
hook is called before the component's ref is set, which causes update to returnfalse
, which in turn causes ProseMirror to destroy and recreate the NodeView, repeating the cycle. The test committed here should illustrate the bug.I'm feeling OK about the fix to the bug, even while I'll admit that I'm not totally sure I understand why this is happening: it's important to tell ProseMirror that an
update
either did update a node (returnstrue
) or did not (returnsfalse
). At this point in the NodeView component code we should have reasonable confidence that this NodeView is responsible for a node at this position, even while we don't have the ref yet and so can't confirm that the Node's type matches the one passed down into the ReactNodeViewWrapper
. By returningtrue
here, a subsequentupdate
cycle is called once theref
has been set, and the logic proceeds as it has been.Some trailing observations:
react-prosemirror
; my guess is that it probably does not, though, because the calling code responsible for destroying/recreating the NodeView in this scenario is non-Reactprosemirror-view.EditorView
code.