slate.js v0.58
依赖版本
返回文档
JSON
"mitt": "^2.1.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-scripts": "3.4.3",
"slate": "^0.58.4",
"slate-react": "^0.58.4"
协同编辑器
返回文档
JSX
import React, { useMemo, useState, useRef, useEffect } from 'react';
// eventEmitter库
import Mitt from 'mitt';
// 导入 Slate 编辑器的工厂函数。
import { createEditor } from 'slate'
// 导入 Slate 组件和 React 插件。
import { Slate, Editable, withReact } from 'slate-react';
// 初始值
const initValue = [{ type: 'paragraph',children: [{ text: 'slate协同编辑 eventEmitter版本 50行代码实现'}]}]
// eventEmitter实例
const emitter = new Mitt();
const SyncEditor = () => {
const editor = useMemo(() => withReact(createEditor()), [])
// 当设置 value 状态时,添加初始化值。
const [value, setValue] = useState(initValue)
// 确定唯一编辑器ID
const id = useRef(`${Date.now()}`)
const onChange = (value) => {
setValue(value)
const ops = editor.operations.filter( op => {
if(op) {
// 过滤 operation,remote的op
return (op.type !== 'set_selection' && op.type !== 'set_node' && !op.remote)
}
return false;
})
// 有操作时才触发
if (ops.length) {
emitter.emit(id.current, ops)
}
}
useEffect(() => {
emitter.on('*', (type, ops) => {
// 触发的id和接收的id不等才有动作
if (id.current !== type) {
for (let i = 0; i < ops.length; i++) {
const op = ops[i];
// 给op加个remote,防止当前editor更新apply了,又再次 emit 到其他编辑器,导致死循环
// 这里slate 0.47的版本apply是同步的,用一个变量就行
op.remote = true;
// 这里也可以用Commands,不过不同命令太多了
editor.apply(op);
}
}
})
}, [])
return (
<div className="slate-syncing-editor">
<Slate editor={editor} value={value} onChange={onChange}>
<Editable />
</Slate>
</div>
)
}
export default SyncEditor;
使用协同编辑器
返回文档
JSX
import React from 'react';
import SyncingEditor from './syncEditor';
function App() {
return (
<div className="App">
<SyncingEditor />
<SyncingEditor />
<SyncingEditor />
</div>
);
}
export default App;
示例
返回文档
Tab-1599212217181.webm正在转码中,请稍候…
Play Pause
Fullscreen Exit fullscreen
00:00 00:00
- 2x
- 1.5x
- 1.25x
- 1x
- 0.75x
- 0.5x
1x
Replay 请刷新试试
源码
返回文档slate-syncing.zip(446.66KB)
slate.js v0.47
依赖版本
返回文档
JSON
"immutable": "^4.0.0-rc.12",
"mitt": "^2.1.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"slate": "^0.47.3",
"slate-react": "^0.22.3",
协同编辑器
返回文档
JSX
import React, { useState, useRef, useEffect } from "react";
import { Editor } from "slate-react";
import Mitt from "mitt";
import { Operation, Value } from "slate";
const initValue = Value.fromJSON({
document: {
nodes: [{
object: "block",
type: "paragraph",
nodes: [
{
object: "text",
leaves: [
{
text: "slate 0.47协同编辑 eventEmitter版本 50行代码实现"
}
]
}
]
}
]}
});
const emitter = new Mitt();
export const SyncingEditor = () => {
const [value, setValue] = useState(initialValue);
const id = useRef(`${Date.now()}`);
const editor = useRef(null);
const remote = useRef(false);
useEffect(() => {
emitter.on("*", (type, ops) => {
if (id.current !== type) {
// 0.47的applyOperation是同步的
remote.current = true;
ops.forEach(op => editor.current!.applyOperation(op));
remote.current = false;
}
});
}, []);
return (
<Editor ref={editor} value={value} onChange={opts => {
setValue(opts.value);
const ops = opts.operations.filter(op => {
if (op) {
return (
o.type !== "set_selection" &&
o.type !== "set_value" &&
(!o.data || !o.data.has("source"))
);
}
return false;
})
.toJS()
.map((o: any) => ({ ...o, data: { source: "one" } }));
if (ops.length && !remote.current) {
emitter.emit(id.current, ops);
}
}}
/>
);
};