Uncaught TypeError: Cannot read properties of null (reading 'matchesNode')
hoebbelsB opened this issue · comments
Please save me some time and use the following template. In 90% of all issues I can't reproduce the problem because I don't know what exactly you are doing, in which environment, or which y-* version is responsible. Just use the following template even if you think the problem is obvious.
Checklist
- Are you reporting a bug? Use github issues for bug reports and feature requests. For general questions, please use https://discuss.yjs.dev/
- Try to report your issue in the correct repository. Yjs consists of many modules. When in doubt, report it to https://github.com/yjs/yjs/issues/
Describe the bug
The syncPlugin will run into an error if the editor get's destroyed. It looks like a call to tiptap#unregisterPlugin
causes every other plugin view to update.
sync-plugin.js
will then schedule a new _forceRenderer()
cycle:
// sync-plugin.js
setTimeout(() => {
binding._forceRerender()
view.dispatch(view.state.tr.setMeta(ySyncPluginKey, { binding }))
}, 0)
When the editor gets destroyed in the meantime, the scheduled function will cause the following error:
index.es.js:4914 Uncaught TypeError: Cannot read properties of null (reading 'matchesNode')
at EditorView.updateStateInner (index.es.js:4914)
at EditorView.updateState (index.es.js:4885)
at Editor.dispatchTransaction (tiptap-core.esm.js:3102)
at EditorView.dispatch (index.es.js:5162)
at sync-plugin.js:282
at ProsemirrorBinding.mux (mutex.js:35)
at ProsemirrorBinding._forceRerender (sync-plugin.js:278)
at sync-plugin.js:134
just noticed,... the same is true for setMeta
which calls eventloop.timeout
:
index.es.js:4914 Uncaught TypeError: Cannot read properties of null (reading 'matchesNode')
at EditorView.updateStateInner (index.es.js:4914)
at EditorView.updateState (index.es.js:4885)
at Editor.dispatchTransaction (tiptap-core.esm.js:3102)
at EditorView.dispatch (index.es.js:5162)
at lib.js:30
at Map.forEach (<anonymous>)
at updateMetas (lib.js:25)
To Reproduce
Steps to reproduce the behavior:
- unregister a plugin right before the editor get's destroyed
- watch
binding._forceRenderer()
throw an error
Expected behavior
setTimeout
handler should get cleared on destroy.
Environment Information
- Browser: Chromium
"y-prosemirror": "^1.0.9"
"y-protocols": "^1.0.5"
"@tiptap/core": "^2.0.0-beta.122"
"@tiptap/extension-collaboration": "^2.0.0-beta.25"
Additional Information
I currently monkeypatch the plugin:
const fragment = this.options.fragment
? this.options.fragment
: this.options.document.getXmlFragment(this.options.field);
const syncPlugin: Plugin = ySyncPlugin(fragment);
let changedInitialContent = false;
let handler: number;
syncPlugin.spec.view = view => {
const binding = new ProsemirrorBinding(fragment, view);
// Make sure this is called in a separate context
if (handler) {
clearTimeout(handler);
}
handler = setTimeout(() => {
binding._forceRerender();
view.dispatch(view.state.tr.setMeta(ySyncPluginKey, { binding }));
}, 0) as unknown as number;
return {
update: () => {
const pluginState = syncPlugin.getState(view.state);
if (pluginState.snapshot == null && pluginState.prevSnapshot == null) {
if (changedInitialContent || view.state.doc.content.findDiffStart(view.state.doc.type.createAndFill().content) !== null) {
changedInitialContent = true;
binding._prosemirrorChanged(view.state.doc);
}
}
},
destroy: () => {
clearTimeout(handler);
binding.destroy();
}
};
};
If I got time, I will prepare a PR for it right away :)
Fixed this in y-prosemirror@10.0.16