editor.js
editor.js copied to clipboard
feat(block): Add option to disable mutation observation
This PR adds the option to disable updates when DOM mutations are observed or inputs are changed inside the element tree of a rendered block. Disabling this behavior is as simple as adding a getter to the block's class:
class ExampleBlock extends BlockTool {
public static get shouldUpdateOnMutation(): boolean {
return false;
}
}
After this, the block should use dispatchChange()
whenever it needs to trigger an update.
Context
I'm currently implementing custom Blocks on a project using React. It uses a portal to render React components inside the wrapper element returned by the render()
method, and it works quite well.
However, every time React updates the DOM inside this portal, the EditorJS instance triggers an update since its MutationObserver is always listening for mutations inside the block. This is undesirable for performance reasons, and this behavior can actually cause loops when interacting with React states.
To fix this, I've added shouldUpdateOnMutation
and every time my internal React component updates its internal data state, it calls dispatchChange()
to tell EditorJS that a change occurred.
The code in this PR is heavily based on the solution found in #2205, created by @medzhidov (thanks, by the way!). I've only changed the name of the option, since my implementation also disables listening for input changes in the block's element tree.
Side note: I intend to release the code that allows users to implement Blocks as React components as a library in the near future. I'll definitely let people know when I do so.
However, every time React updates the DOM inside this portal, the EditorJS instance triggers an update since its MutationObserver is always listening for mutations inside the block.
Have you tried adding the data-mutation-free
to some elements that are changing in this case?
However, every time React updates the DOM inside this portal, the EditorJS instance triggers an update since its MutationObserver is always listening for mutations inside the block.
Have you tried adding the
data-mutation-free
to some elements that are changing in this case?
That didn't work for me, so this is the reason why I wrote this code
Could you provide an example of exact dom changes in your tool that triggers unwanted onChange
? Maybe we can improve logic related to the data-mutation-free
to handle that.
Have you tried adding the
data-mutation-free
to some elements that are changing in this case?
@neSpecc data-mutation-free
works, but since all mutated nodes need to have this property and React might mutate various nodes when rendering, including nodes unrelated to rendered content, the solution would be adding this property to a lot of elements depending on the block's complexity. I believe this is undesirable since it has the potential to pollute an application code, specially in complex blocks.
Could you provide an example of exact dom changes in your tool that triggers unwanted
onChange
? Maybe we can improve logic related to thedata-mutation-free
to handle that.
Absolutely. I'll create an example in the next few days, and I'll mention you in the comment when I do so.
Regarding improving data-mutation-free
's logic, it's a possibility. In a way, this PR adds a data-mutation-free="deep"
option of sorts.
Instead of using a class property (like I did in this PR), we could simply update data-mutation-free
to allow developers to choose between ignoring mutations on a single element (which could be the default for backwards compatibility) or ignoring mutations on an element and all of their children (like the example above, data-mutation-free="deep"
).
We could also just add a new property to allow the second option, like data-deep-mutation-free
.
Which do you think is the better option?
That would be so nice if that worked, i am rendering a few custom Vue components as Block Tools, for example a Video.js player, when i just play the video the editorjs gets a "block-changed" event and that's simply not true.
@lucas-varela can you please update your PR and resolve the conflicts - so i can use your fork ;)
@bettysteger I'm currently busy working with infrastructure matters on my project, and I sadly don't have the time necessary to update this PR in the foreseeable future, since there's been plenty of changes since I've opened it.
However, one of my peers recently fixed an unrelated bug on our project by updating editor.js to version 2.28.2, and by doing so, they switched from my fork (with the mutation observer fix) to the official package. Everything seems to be working fine, and DOM mutations caused by React aren't triggering dozens of editor.js updates or loops like it did before.
I believe some editor.js update might have fixed the behavior, but I didn't have an opportunity to take a closer look at the changes to make sure this behavior is 100% fixed and won't cause trouble when interacting with libs like React and Vue in the future, but I'm planning to do so. But, again, I'm not sure when I'll do this.
For now, please try to update to v2.28.2 if you didn't already do so because it might fix this behavior. Good luck!