反应 useRef() 没有被定义
React useRef() not getting defined
我有一个基于 prosemirror 的编辑器,我想在上面启用实时协作。所以我将套接字服务器设置为 described here
我将 WebsocketProvider
设置为 useRef()
,这样我们就不会在每次渲染组件时都不断地重新创建它(在我启动数十个 websocket 之前)。但是,现在它甚至没有被定义,因为 get plugins()
中的 console.log(this.yXmlFragment, this.provider)
返回为 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 []
}
};
发生这种情况是因为您将 sectionID
包含在 EditorContainer
中 useEffect
的依赖项列表中。因为 sectionID
在初始加载时不会改变,所以这个 useEffect
永远不会触发。我在这里创建了一个最小的例子:
https://codesandbox.io/s/focused-babbage-ilx4j?file=/src/App.js
我建议将 useEffect
更改为 useMemo
,因为它在 EditorContainer
的初始渲染时至少运行一次。而且您仍然可以获得性能优势,因为它不应重新运行,除非 sectionID
发生变化。
我有一个基于 prosemirror 的编辑器,我想在上面启用实时协作。所以我将套接字服务器设置为 described here
我将 WebsocketProvider
设置为 useRef()
,这样我们就不会在每次渲染组件时都不断地重新创建它(在我启动数十个 websocket 之前)。但是,现在它甚至没有被定义,因为 get plugins()
中的 console.log(this.yXmlFragment, this.provider)
返回为 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 []
}
};
发生这种情况是因为您将 sectionID
包含在 EditorContainer
中 useEffect
的依赖项列表中。因为 sectionID
在初始加载时不会改变,所以这个 useEffect
永远不会触发。我在这里创建了一个最小的例子:
https://codesandbox.io/s/focused-babbage-ilx4j?file=/src/App.js
我建议将 useEffect
更改为 useMemo
,因为它在 EditorContainer
的初始渲染时至少运行一次。而且您仍然可以获得性能优势,因为它不应重新运行,除非 sectionID
发生变化。