0

我有一个基于 prosemirror 的编辑器,我想启用实时协作。所以我设置了一个套接字服务器,如此处所述

我将其设置WebsocketProvider为 auseRef()这样我们就不会在每次渲染组件时都不断地重新创建它(在我启动数十个 websocket 之前)。但是,现在它甚至没有被定义,因为console.log(this.yXmlFragment, this.provider)inget plugins()都返回为undefined

我想要的行为是我想要provider并且yXmlFragment仅在sectionID更改时更新,而不是任何其他重新渲染。但它甚至不是第一次设置。谁能解释我错了?

import React, { useContext, useEffect, useRef } from "react";
import Editor, { Extension } from "rich-markdown-editor";
import * as Y from 'yjs';
import { WebsocketProvider } from 'y-websocket';
import { ySyncPlugin, yCursorPlugin } from 'y-prosemirror';
import { AuthUserContext } from "../Util/AuthUser";

type EditorInput = {
  id?: string;
  readOnly?: boolean;
  defaultValue?: string;
  value?: string;
  placeholder?: string;
  sectionID?: string;
  onChange(e: string): void;
};

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

  const provider = useRef<WebsocketProvider>();
  const yXmlFragment = useRef<Y.XmlFragment>();
  
  useEffect(() => {
    const ydoc = new Y.Doc();
    yXmlFragment.current = ydoc.getXmlFragment('prosemirror');
    provider.current = new WebsocketProvider('wss://my_socket_server.herokuapp.com', `${sectionID}`, ydoc);
  }, [sectionID]);
  
  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, currentUser)
        ]}
      />
    </div>
  );
};

export default EditorContainer;

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

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

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

    this.provider = provider;
    this.yXmlFragment = yXmlFragment;
    this.currentUser = currentUser;
  }

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

  get plugins() {
    console.log(this.yXmlFragment, this.provider);
    if (this.yXmlFragment && this.provider) {
      this.provider.awareness.setLocalStateField('user', {
        name: currentUser.name,
        color: '#1be7ff',
      });
      return [
        ySyncPlugin(this.yXmlFragment),
        yCursorPlugin(this.provider.awareness),
      ]
    }
    return []
  }
};
4

1 回答 1

1

发生这种情况是因为您包含sectionID在 inside 的依赖项列表useEffectEditorContainer。因为sectionID在初始加载时不会改变,所以它useEffect永远不会触发。我在这里创建了一个最小的例子: https ://codesandbox.io/s/focused-babbage-ilx4j?file=/src/App.js

我建议更改useEffect为,useMemo因为它在初始渲染时至少运行一次EditorContainer. sectionID而且您仍然可以获得性能优势,因为除非发生更改,否则它不应重新运行。

于 2021-04-20T20:48:12.217 回答