Infinite loop of updates caused in rare situation with React

I’m a novice when it comes to CRDT, so I’m just curious how this could happen given CRDT?

Yjs works great under normal circumstances, but a rare situation with React causes an infinite loop of updates where “A” becomes “AA” then “AAA” and so on. It only happens when using React dev server, not in production. Based on my logs, none of my code is called. The only place where my code inserts text into a document is in Binding and it is not called when the infinite loop happens.

I’m using Yjs, quill, y-quill, y-websocket, react-quill, quill-cursors.

In my app, quill editors are opened and destroyed frequently. This is not a problem. The only problems occur when the dev servers are restarted. Somehow Yjs gets into a state where it starts updating the doc without any user input and each update then triggers another update, creating an infinite loop.

I suspect that this is caused if doc has multiple bindings, but I would still think CRDT would prevent this. Why doesn’t it?

Any insights of what would cause this scenario would be much appreciated?

Hi @mattch,

It is perfectly fine to have multiple editors open with y-quill bindings. But it is likely that you forgot to destroy one of them. This might be due to reacts magical hot-reloading feature that replaces components under the hood.

There is likely an infinite loop somewhere in your codebase where binding1 updates an editor, this change is recognized by binding2, which will update the yjs type, which will trigger binding1 again to perform an update on the editor… and so on…

You shouldn’t have multiple bindings to the same editor.

Thanks, Kevin! Really appreciate what you have done with Yjs.

Yes, definitely related to the hot-reloading. I have quill wrapped in a functional component and only do the binding when it’s mounted and do a destroy when it’s unmounted. I also wrap it in a useMemo() so it’s not rerendered to prevent cursor repositions.

I suspect the hot-reloading must be re-mounting without ever unmounting the original component. This could cause the same editor to be bound twice.

Is there a way to detect if an editor is already bound (QuillBinding)?

1 Like

Not unfortunately not. But you could store the binding instance on the quill editor instance using private name. E.g. quill._yquillBinding?.destroy(); quill._yquillBinding = new QuillBinding(..).

@dmonad Thanks for the idea. I was able to get that to work though there was some weirdness with storing/retrieving the binding from the editor.