ReactJS + QuillJS

Hi there-

I have a “working” solution for a live notes page using React, Quilljs, Yjs, y-websocket and y-quill.

Though the document is connecting and does seem to receive updates successfully, I am constantly receiving disconnect errors and sometimes the document does not connect at all.

Below is my current solution- is there anything within that may be an indicator of what is wrong?

import React, { useState } from "react";
import * as Y from "yjs";
import { WebsocketProvider } from "y-websocket";
import { QuillBinding } from "y-quill";


function LiveNotes(props: any) {
  React.useEffect(() => {
    if (!connProvider) return;
    connProvider.on("connection-error", (event: any) => {
      connProvider.disconnect();
      messageApi.error("Error connecting to note.");
      quill.setText("Error connecting to document.");
    });
  });

  // Start Connection. I also create a Y.doc on the serverside (assuming that is best practice).
  React.useEffect(() => {
    if (quill == null || documentId == undefined) return;
    if (!binding) {
      const ydoc = new Y.Doc();
      const fakeID = "";
      const provider = new WebsocketProvider(
        `${websocketURL}?Authorization=${CONTEXT.oktaToken}&Subscription-Key=${SUBSCRIPTION_KEY}&type=note&note_id=${documentId}&user_token=${userToken}`,
        fakeID,
        ydoc
      );
      setConnProvider(provider);
      setTempYDoc(ydoc);

      // User is authenticated within initial connect. Document should also load from db.
      if (provider.ws) {
        setAuthenticated(true);
        quill.enable();
      }
    }
  }, [quill, documentId]);

  // Periodically send updates to db:
  const sendDataSave: any = React.useCallback(
    debounce((contents: any, prov: any, doc: any) => {
      prov?.ws?.send(
        JSON.stringify({
          type: "document_save",
          document: contents,
          bytesDoc: fromUint8Array(Y.encodeStateAsUpdate(doc)),
        })
      );
    }, 1000),
    []
  );
  
 // Update server after all else is established
  React.useEffect(() => {
    if (
      !authenticated ||
      !tempYDoc ||
      !connProvider ||
      !quill ||
      !historyLoaded
    )
      return;

    tempYDoc.on("update", (update: any) => {
      // @ts-ignore
      if (origin !== this && origin !== null) {
        sendDataSave(quill.getText(), connProvider, tempYDoc);
      }
    });
  }, [tempYDoc, quill, authenticated, connProvider, historyLoaded]);

  // Bind textbox and user
  React.useEffect(() => {
    if (authenticated && connProvider) {
      const ytext = tempYDoc.getText("quill");
      const binding = new QuillBinding(ytext, quill, connProvider.awareness);

     // Set the username for awareness
      connProvider.awareness.setLocalStateField("user", {
        name: userProfile.email,
        color: usercolors[Math.floor(Math.random() * usercolors.length)],
      });

      setBinding(binding);
      setAwareness(connProvider.awareness);
    }
  }, [authenticated, connProvider]);

  return (
     ....notes container
  );
}

export default LiveNotes;

There’s a bit of code there so it’s hard to say if something else is going wrong, but I’ve noticed this rather problematic behavior of Yjs generating extraneous updates when doc is loaded Extraneous updates where origin == null when using full reload · Issue #157 · yjs/y-websocket · GitHub

These updates in some cases have origin === this which seem to get filtered and not sent to server. However, as I tried and it seems you are doing as well, you can’t filter out updates that have origin === null. For some reason if you do, the state becomes inconsistent between the client and the server and any other clients connected won’t receive the correct state anymore.

I recommend removing that check and trying again. It would be nice if there were more eyes on the actual problem to figure out what’s happening.

Are you using the React-Quill?