在 Plate 中实现完全受控的编辑器值较为复杂,原因如下:
-
编辑器状态不仅包含内容 (
editor.children),还包含editor.selection和editor.history。 -
直接替换
editor.children可能会破坏选区(selection)和历史记录(history),导致意外行为或崩溃。 -
所有对编辑器值的更改都应通过 Transforms 进行,以保持与选区(selection)和历史记录(history)的一致性。
鉴于这些挑战,通常建议将 Plate 作为非受控输入使用。但如果需要从外部更改编辑器内容,可以使用 editor.tf.setValue(value) 函数。
性能考量
使用 editor.tf.setValue 会在每次调用时重新渲染所有节点,因此应谨慎使用。
如果频繁调用或处理大型文档,可能会影响性能。
或者,您可以使用 editor.tf.reset() 来重置编辑器状态,这将同时重置选区(selection)和历史记录(history)。
异步初始值
您可以使用 skipInitialization 延迟编辑器初始化,直到异步数据准备就绪。然后调用 editor.tf.init 并传入您的值:
function AsyncControlledEditor() {
const [initialValue, setInitialValue] = React.useState();
const [loading, setLoading] = React.useState(true);
const editor = usePlateEditor({
skipInitialization: true,
});
React.useEffect(() => {
// 模拟异步获取
setTimeout(() => {
setInitialValue([
{
type: 'p',
children: [{ text: '已加载异步值!' }],
},
]);
setLoading(false);
}, 1000);
}, []);
React.useEffect(() => {
if (!loading && initialValue) {
editor.tf.init({ value: initialValue, autoSelect: 'end' });
}
}, [loading, initialValue, editor]);
if (loading) return <div>加载中…</div>;
return (
<Plate editor={editor}>
<EditorContainer>
<Editor />
</EditorContainer>
</Plate>
);
}Initial Value
Async Controlled Editor
Loading…
Files
'use client';
import * as React from 'react';
import { Plate, usePlateEditor } from 'platejs/react';
import { Button } from '@/components/ui/button';
import { Editor, EditorContainer } from '@/components/ui/editor';
export default function ControlledEditorDemo() {
const editor = usePlateEditor({
value: [
{
children: [{ text: 'Initial Value' }],
type: 'p',
},
],
});
return (
<div>
<Plate editor={editor}>
<EditorContainer>
<Editor className="px-0" />
</EditorContainer>
</Plate>
<div className="mt-4 flex flex-col gap-2">
<Button
onClick={() => {
// Replace with HTML string
editor.tf.setValue([
{
children: [{ text: 'Replaced Value' }],
type: 'p',
},
]);
editor.tf.focus({ edge: 'endEditor' });
}}
>
Replace Value
</Button>
<Button
onClick={() => {
editor.tf.reset();
editor.tf.focus();
}}
>
Reset Editor
</Button>
</div>
<hr className="my-8" />
<h2 className="mb-2 text-lg font-semibold">Async Controlled Editor</h2>
<AsyncControlledEditorDemo />
</div>
);
}
function AsyncControlledEditorDemo() {
const [initialValue, setInitialValue] = React.useState<
{ children: { text: string }[]; type: string }[] | undefined
>(undefined);
const [loading, setLoading] = React.useState(true);
const editor = usePlateEditor({
skipInitialization: true,
});
React.useEffect(() => {
// Simulate async fetch
setTimeout(() => {
setInitialValue([
{
children: [{ text: 'Loaded async value!' }],
type: 'p',
},
]);
setLoading(false);
}, 1000);
}, []);
React.useEffect(() => {
if (!loading && initialValue) {
editor.tf.init({ autoSelect: 'end', value: initialValue });
}
}, [loading, initialValue, editor]);
if (loading) return <div>Loading…</div>;
return (
<Plate editor={editor}>
<EditorContainer>
<Editor className="px-0" />
</EditorContainer>
</Plate>
);
}
controlled-demo


On This Page
异步初始值