tiptap: TypeError: Cannot read property 'matchesNode' of null (TipTap for React)

Description

Same issue as in https://github.com/ueberdosis/tiptap/issues/438. TypeError that crashes my app.

Steps to reproduce the bug

Happens on occasion when navigating to a page with a TipTap editor on it.

CodeSandbox

In the CSB below, run the tests, and observe the console spits out a bunch of these type errors for the browser. I don’t know why it does this…

https://codesandbox.io/s/tiptap-prose-matchesnode-error-yhvdc

image

Expected behavior

No error.

Environment?

  • operating system: OSX
  • browser: N/A
  • mobile/desktop: N/A
  • tiptap version: @tiptap/core@^2.0.0-beta.65

Additional context

I tried to keep the CSB example above as close to my current code as possible, minus other third-party libraries that would unnecessarily bloat the example.

I am able to replicate the error consistently in my tests:

    Error: Uncaught [TypeError: Cannot read property 'matchesNode' of null]
        at reportException (/Users/jasonhughes/sites/innovator-portal/innovator/client/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:62:24)
        at Timeout.task [as _onTimeout] (/Users/jasonhughes/sites/innovator-portal/innovator/client/node_modules/jsdom/lib/jsdom/browser/Window.js:521:9)
        at listOnTimeout (internal/timers.js:549:17)
        at processTimers (internal/timers.js:492:7) TypeError: Cannot read property 'matchesNode' of null
        at EditorView.updateStateInner (/Users/jasonhughes/sites/innovator-portal/innovator/client/node_modules/prosemirror-view/src/index.js:141:45)
        at EditorView.updateState (/Users/jasonhughes/sites/innovator-portal/innovator/client/node_modules/prosemirror-view/src/index.js:114:10)
        at Editor.dispatchTransaction (/Users/jasonhughes/sites/innovator-portal/innovator/client/node_modules/@tiptap/core/src/Editor.ts:299:15)
        at EditorView.dispatch (/Users/jasonhughes/sites/innovator-portal/innovator/client/node_modules/prosemirror-view/src/index.js:374:50)
        at Object.method [as focus] (/Users/jasonhughes/sites/innovator-portal/innovator/client/node_modules/@tiptap/core/src/CommandManager.ts:35:18)
        at /Users/jasonhughes/sites/innovator-portal/innovator/client/node_modules/@tiptap/core/src/Editor.ts:90:21
        at Timeout.task [as _onTimeout] (/Users/jasonhughes/sites/innovator-portal/innovator/client/node_modules/jsdom/lib/jsdom/browser/Window.js:516:19)
        at listOnTimeout (internal/timers.js:549:17)
        at processTimers (internal/timers.js:492:7)

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 3
  • Comments: 36 (7 by maintainers)

Commits related to this issue

Most upvoted comments

Hey @philippkuehn, I think I’ve found a more general solution to this that prevents the error. A little hacky but let me know what you think

import { EditorView } from 'prosemirror-view'

EditorView.prototype.updateState = function updateState(state) {
  if (!this.docView) return // This prevents the matchesNode error on hot reloads
  this.updateStateInner(state, this.state.plugins != state.plugins)
}

We were able to hack around this by adding checks for editor.isDestroyed in our useEffect hooks like so: image

Could we update editor.commands to do this check automatically, and never try to run a command when the editor itself is destroyed? That might catch a lot of issues.

However, even after adding this, hot reload still triggered an error in the Yjs sync plugin, see screenshot below. It looks like that can also be suppressed by adding // @refresh reset at the top of the file where we load the Collaboration plugin. This is a NextJS directive, not sure if there are equivalents elsewhere.

Screen Shot 2021-07-06 at 2 21 29 PM

This patch should fix that error.

  useEffect(() => {
    if (editor && !editor.isDestroyed) {
      editor.chain().focus().setContent(content).run();
    }
  }, [content, editor]);

can solve this problem.

This patch should fix that error.

I am using Remix v2 and have the same problem on every hot reload. I use useEditor(). I did not see a solution/workaround for people using that hook, maybe someone solved this though?

Yeah I’m still noticing this as obezzad mentioned, even after upgrading to the patch with the fix.

I noticed that the same error also occurs on setContent after React’s Fast Refresh.

I’ve just followed the official documentation for installing TipTap into NextJS and got this bug. Latest TipTip and NextJS. I just copy pasted the example code without adding anything.

I tried all fixes and nothing seems to work. (I’m not sure if I was doing correctly.) Whenever I change something in the component I see this error in the browser.

Is there any other was I could make this work?

@philippkuehn Thank you for the patch! I noticed that the same error also occurs on setContent after React’s Fast Refresh.

Hey @philippkuehn, I think I’ve found a more general solution to this that prevents the error. A little hacky but let me know what you think

import { EditorView } from 'prosemirror-view'

EditorView.prototype.updateState = function updateState(state) {
  if (!this.docView) return // This prevents the matchesNode error on hot reloads
  this.updateStateInner(state, this.state.plugins != state.plugins)
}

where do you put this line? I am getting following error . Property 'docView' does not exist on type 'EditorView' & Property 'updateStateInner' is private and only accessible within class 'EditorView'

@vaetas Assuming your issue is caused by the same root cause as the issue I submitted, it was fixed in v2.0.0-beta.220!

This error also seems to be triggered by StrictMode, because the component is immediatly unmounted and remounted.

@chasinhues I hope the following would help spotting the root cause.

This issue happens to me in editor.setContent() after hot-reload and when creating an editor dynamically, it also happens when dynamically adding multiple editors without delay. If I wait for a second after creating an editor before re-creating another one, it doesn’t occur.

It seems that successive dynamic creation error is due to setTimeout(() => editor.createNodeViews(), 0) in EditorContent. I also suspect that this error causes the setContent() one too.

image

I added try-catch around setContent() with 3 retries separated by a delay of 100ms. However, the createNodeViews() does not seem to be catchable from the component.

Is there any chance of a hotfix to prevent this uncaught error from propagating since the software will be shipped soon 😦 ?

Same issue on my end

Updated all my dependencies to the latests to try out, the issue still shows up.

For me it starts at "@tiptap/react": "^2.0.0-beta.43", which introduces the dependency array for the editor.

This error also seems to be triggered by StrictMode, because the component is immediatly unmounted and remounted.

The remix app was also giving an error on every hot reload due to StrictMode, removing StrictMode worked fine. But I guess that doesn’t make sense for StrictMode, lol.

I encountered this issue when adding dependencies to useEditor from @tiptap/react. I’m running "@tiptap/react": "^2.0.0-beta.199"

If I don’t add the dependencies to the array I don’t get this error when I have multiple editors on the same page. As I add deps to get it to react to being moved/destroyed I get this error. I

I used #1451 (comment) solution and removing the dependency array. This seems like a bug to me, I’m just getting a less granular solution to what I assume useEditor does under the hood.

It has been almost two years and there is no solution in sight.

For me the error also came with a modal function where the editor was not visible.

I solved the problem by checking if the plugins are loaded.

export const resetContent = () => {
		if (editor.view.pluginViews.length) {
			editor.commands.clearContent();
		}
	};

I modified the code by @thatsjonsense to reuse the old updateState:

import { EditorView } from 'prosemirror-view';

const oldUpdateState = EditorView.prototype.updateState;

EditorView.prototype.updateState = function (state) {
  // This prevents the matchesNode error on hot reloads
  if (!this.docView) {
    return;
  }

  oldUpdateState.call(this, state);
};

This bug was apparently fixed in y-prosemirror@1.1.0, but this same version introduced another issue: https://github.com/yjs/y-prosemirror/issues/117

So I had to downgrade to y-prosemirror@1.0.20 and this problem still exists there.