微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

React useRef() 未定义

如何解决React useRef() 未定义

我有一个基于 prosemirror 的编辑器,我想在上面启用实时协作。所以我将套接字服务器设置为 described here

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

的形式返回

我想要的行为是我希望 provideryXmlFragment 仅在 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 []
  }
};

解决方法

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

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。