Node decorations before custom node view causes rerender

I am running into problems with the interaction between a node decoration and a custom nodeView. When the node decoration is before the custom nodeview, and is then removed (i.e. replaced with an DecorationSet.empty or a DecorationSet that has no decorations), the entire document rerenders (and the node view is recreated).

In this case, my decoration adds a class to empty paragraphs (a prompt for users) (see github) and my node-view is an iframe (see github). The behaviour means the iframe gets rerendered on selection changes (causing flicking/loading, and other more complicated node views get recreated as well).

This seems to only happen when the prompt decoration is removed when it is before the iframe in the document, and when also involved in a selection change (it doesn’t happen when there are document changes). An example is below (also online here):

iframe-decoration-bug

I was wondering if a quick look at the decoration code below highlights the problem? Or if maybe this is a bug? Or expected behaviour? I have also tried using the DecorationSet to map/add/remove the decorations. When it gets down to an empty set, that always seems to trigger a full refresh and flicker in the IFrame/custom nodeView.

I also think this may have been introduced in the last four or so weeks, (this code did work before, but I am having trouble reproducing that and getting back to the right versions; our deployed app doesn’t do this). I can get a minimal example together to demonstrate this if it is helpful/worth it.

Decoration code

const promptPlugin = new Plugin({
  key,
  state: {
    init: () => DecorationSet.empty,
    apply(tr, value, oldState, newState) {
      if (!isEditable(newState)) return DecorationSet.empty;
      const paragraph = getParentIfParagraph(newState.selection);
      const emptyParagraph = paragraph && paragraph.node.nodeSize === 2;
      if (tr.selection.empty && emptyParagraph) {
        const deco = Decoration.node(tr.selection.from - 1, tr.selection.to + 1, {
          class: 'prompt',
        });
        return DecorationSet.create(tr.doc, [deco]);
      }
      return DecorationSet.empty;
    },
  },
  props: {
    decorations(state) {
      return this.getState(state);
    },
  },
});

I would try putting a console.log statement in the NodeView.update method for the iframe and seeing if update is called when that nearby prompt decoration is changed, just to double check whether its the decoration causing an update.

Thanks! Tried adding that: the update method is not called when the decoration is removed.

Removing the decoration plugin completely means the node-view is rendered as expected (only when the node-properties change). This also doesn’t happen if I leave the decorations in place and do not remove them.

Hi, your example link points to what appears to be a project page. Could you distill this problem down to a minimal script so I can debug it?

1 Like

Thank you! I will get a simple example one together over this weekend.