ueberdosis / hocuspocus

The CRDT Yjs WebSocket backend for conflict-free real-time collaboration in your app.
https://tiptap.dev/docs/hocuspocus/introduction
MIT License
1.21k stars 117 forks source link

Hocuspocus >=2.7.0 does not work with React in strict mode / Lexical #760

Open GermanJablo opened 9 months ago

GermanJablo commented 9 months ago

Description As of version 2.7.0 hocuspocus does not synchronize if react is in strict mode.

Steps to reproduce the bug

  1. Clone https://github.com/GermanJablo/collab
  2. run npm run dev and in other console npm run collab-server
  3. Try to write to the document or switch between documents 1 and 2, and you will see that they do not sync.
  4. modify the reactStrictMode property of nextjs.config.ts to false and it will work, or alternatively downgrade hocuspocus to version 2.6.1.

Environment?

Ramnath-Karthikesan commented 8 months ago

I encountered a similar challenge while integrating the lexical editor with a collaboration plugin using the hocus pocus provider. While the collaboration worked seamlessly between two tabs in the browser, attempting the same on a regular tab and in incognito mode resulted in unsynchronized changes.

I also tried connecting two different computers to the same network with the help of mobile network and then tried using the editor with collab but the changes did not sync.

Once I remove the strict mode on my react app everything worked as expected. I also tried downgrading to version 2.4.0 and the collaboration worked with the strict mode on.

janthurau commented 8 months ago

@GermanJablo I just see that you have connect: false passed to the websocket provider - does it work if you remove that? Earlier versions also connected when connect: false was passed, but we fixed that.

GermanJablo commented 8 months ago

No, that's not the problem, it still doesn't work removing that line :)

janthurau commented 8 months ago

hmmm, not sure what's the issue here to be honest. The providers don't even emit the synced event.

I think the issue is that both providers bind to the socket, the 1st gets destroyed, then unbinds from the socket, and then also unbinds the 2nd (because they are using the same document name). We changed something there with 2.7.0 to make it (a lot) more performant. Not sure what's the fix here, apart from making sure that the provider gets created just once.

ilya2204 commented 7 months ago

I started having similar problems when I call provider.disconnect() and then after some time provider.connect(). This is similar to what can happen in React's strict mode. (I also pass preserveConnection: false to the constructor)

But I have this problem since version 2.6.0, maybe you can check this version for yourself too?

I have a suspicion about this PR, but these are just my guesses