Duplication of content when using y-websockets

Hi folks, I’m new here and just digging into CRDT and yjs. I’ve got my own forked version of a prosemirror editor and looking to add collaborative editing

Everything works as expected - e.g., change are propagated, there’s a cursor with my name on it in each editor - except when I reload the page. When I reload, the content in my editor always duplicates

Has anyone else run into this? Or know what pattern of things I might be doing wrong? Thanks so much

I’m using React/TS if that’s helpful

Code is below

const EditorContainer: React.FC<EditorInput> = (props) => {
  const {
    id,
    readOnly,
    placeholder,
    sectionID,
    defaultValue,
    value,
    onChange,
  } = props;
  const { currentDBUser } = useContext(AuthUserContext);

  const provider = useRef<WebsocketProvider>();
  const yXmlFragment = useRef<Y.XmlFragment>();
  
  useMemo(() => {
    const ydoc = new Y.Doc();
    yXmlFragment.current = ydoc.getXmlFragment('prosemirror');
    provider.current = new WebsocketProvider('wss://my-y-websockets.herokuapp.com', `${sectionID}`, ydoc);

    if (provider.current.shouldConnect) {
      provider.current.connect()
    } else {
      provider.current.disconnect()
    }

    return () => {
      provider.current?.disconnect()
    }
  }, [sectionID]);
  
  if (currentDBUser) {
    return (
      <div className="Editor-Container" id={id}>
        <Editor
          onChange={(e: any) => onChange(e)}
          defaultValue={defaultValue}
          value={value}
          readOnly={readOnly}
          placeholder={placeholder}
          extensions={[
            new yWebsocketExt(provider.current, yXmlFragment.current, currentDBUser)
          ]}
        />
      </div>
    );
  }
  return <></>
};

export default EditorContainer;

class yWebsocketExt extends Extension {
  provider?: WebsocketProvider;
  yXmlFragment?: Y.XmlFragment;
  currentDBUser: User;

  constructor(provider: WebsocketProvider | undefined, yXmlFragment: Y.XmlFragment | undefined, currentDBUser: User) {
    super();

    this.provider = provider;
    this.yXmlFragment = yXmlFragment;
    this.currentDBUser = currentDBUser;
  }

  get name() {
    return "y-websocket sync";
  }

  get plugins() {
    if (this.yXmlFragment && this.provider) {
      this.provider.awareness.setLocalStateField('user', {
        name: this.currentDBUser ? `${this.currentDBUser.firstName} ${this.currentDBUser.lastName}` : `Random Monkey`,
        color: usercolors[Math.floor(Math.random() * usercolors.length)],
      });
      return [
        ySyncPlugin(this.yXmlFragment),
        yCursorPlugin(this.provider.awareness),
      ]
    }
    return []
  }
};

Welcome to the discussion board @chumbalaya,

It seems you are inserting the content every time you load the editor (see value).

A related discussion that might be interesting to you: Initial offline value of a shared document

Cheers,
Kevin

1 Like

Makes total sense, thanks @dmonad! Super easy fix