BlockNote icon indicating copy to clipboard operation
BlockNote copied to clipboard

Large ydoc using yprovider causing the blocknote to crash

Open lawrencenika opened this issue 11 months ago • 2 comments

Describe the bug hook.js:608 Caught error while handling a Yjs update Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops. at throwIfInfiniteUpdateLoopDetected (react-dom.development.js:26793:11) at getRootForUpdatedFiber (react-dom.development.js:7627:3) at enqueueConcurrentRenderForLane (react-dom.development.js:7549:10) at forceStoreRerender (react-dom.development.js:12049:14) at handleStoreChange (react-dom.development.js:12028:7) at eval (blocknote-react.js:2687:27) at Set.forEach () at Object.setRenderer (blocknote-react.js:2687:12) at ReactRenderer.render (index.js:1380:128) at eval (index.js:1358:22) at flushSync$1 (react-dom.development.js:24987:14) at flushSync (react-dom.development.js:38540:10) at new ReactRenderer (index.js:1357:65) at ReactNodeView.mount (index.js:1471:25) at new NodeView (index.js:5209:14) at new ReactNodeView (index.js:1410:1) at eval (index.js:1625:16) at eval (blocknote-react.js:2968:10) at nodeview (index.js:1452:37) at NodeViewDesc.create (index.js:1288:30) at ViewTreeUpdater.addNode (index.js:1937:33) at eval (index.js:1392:25) at iterDeco (index.js:2053:13) at NodeViewDesc.updateChildren (index.js:1371:9) at ViewTreeUpdater.addNode (index.js:1939:18) at eval (index.js:1392:25) at iterDeco (index.js:2053:13) at NodeViewDesc.updateChildren (index.js:1371:9) at NodeViewDesc.updateInner (index.js:1467:18) at NodeViewDesc.update (index.js:1459:14) at ViewTreeUpdater.updateNextNode (index.js:1894:37) at eval (index.js:1389:30) at iterDeco (index.js:2053:13) at NodeViewDesc.updateChildren (index.js:1371:9) at NodeViewDesc.updateInner (index.js:1467:18) at NodeViewDesc.update (index.js:1459:14) at EditorView.updateStateInner (index.js:5407:45) at EditorView.updateState (index.js:5358:14) at we.dispatchTransaction (index.js:4805:19) at EditorView.dispatch (index.js:5725:33) at eval (sync-plugin.js:593:28) at ProsemirrorBinding.eval [as mux] (mutex.js:39:9) at ProsemirrorBinding._typeChanged (sync-plugin.js:555:10) at Module.callAll (function.js:37:12) at callEventHandlerListeners (yjs.mjs:2110:47) at eval (yjs.mjs:3416:13) at Map.forEach () at Array.eval (yjs.mjs:3397:40) at callAll (function.js:37:12) at cleanupTransactions (yjs.mjs:3421:62)

To Reproduce use the similar code in ypartykit

async onConnect(connection: Party.Connection) {
        return onConnect(connection, this.room, {
            load: async () => {
                // This is called once per "room" when the first user connects

                const doc = new Y.Doc();

                const response = await fetch(
                    url,
                    {
                        method: 'GET',
                        headers: {
                            'Accept': 'application/json',
                        },
                    },
                );

                if (!response.ok) {
                    const errorDetails = await response.text();
                    console.error(
                        `Failed to load ydoc from persistent database: ${response.status}: ${response.statusText}\nDetails: ${errorDetails}`,
                    );
                    return doc;
                }

                const data = await response.json();
                if (data.document) {
                    Y.applyUpdate(doc, Buffer.from(data.document, 'base64'));
                }

                return doc;
            },
            callback: {
                handler: async (yDoc) => {
                   ...
            },
        });
    }

and nextjs code:

const editor = useCreateBlockNote(
        {
            collaboration: {
                provider,
                fragment: doc.getXmlFragment('document-store'),
                user: {
                    name: mobxstore.user.userInfo.username,
                    color: userColor,
                },
            },
            schema: customSchema,

            _tiptapOptions: {
                extensions: [
                    Extension.create({
                        name: 'customKeyEvents',
                        priority: 1000,
                        addProseMirrorPlugins() {
                            return [
                                new Plugin({
                                    key: new PluginKey('customKeyEvents'),
                                    props: {
                                        handleKeyDown: (view, event) =>
                                            handleKeyDown(event),
                                    },
                                }),
                            ];
                        },
                    }),
                ],
            },
        },
        [doc, provider],
    );

Misc

  • Node version: v20.9.0
  • Package manager: either of blocknote version tried are 0.24.2 and v.0.15.3
  • Browser: Arc ir Chrome
  • [ ] I'm a sponsor and would appreciate if you could look into this sooner than later 💖

lawrencenika avatar Mar 04 '25 08:03 lawrencenika

Thanks for reporting. Would it be possible to create a fully reproducible example of this? ( Best is to provide an online sandbox, click to create one )

YousefED avatar Mar 04 '25 13:03 YousefED

quite hard to share a fully useable sandbox because my codebase is too large now. But what I realize is that I created a custom codeblock schema to use in blocknote with yjs. And in one page at once there can be tens of codeblocks showing at the same time. My suspicion is that they are all binded to the yjs update each, causing the react stack to be unable to handle so many of them all at once. Could this be possible?

lawrencenika avatar Mar 04 '25 16:03 lawrencenika

I'm going to close this as we need a reproducible example to diagnose further

YousefED avatar Jun 20 '25 07:06 YousefED