You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PlateJS HTML序列化问题:点击按钮无法获取有效HTML字符串

问题解决:Plate编辑器获取HTML并集成React-Hook-Form

问题原因

你当前代码里的html变量是组件初始化时计算的,此时编辑器内容还未更新,后续编辑操作不会重新计算这个变量,所以点击按钮输出的始终是初始的空字符串。正确的做法是在需要获取HTML的时机(比如点击按钮、表单提交)实时调用serializeHtml,基于最新的编辑器内容生成HTML。

修正后的基础版本代码

"use client";

import { plugins } from "@/lib/plugins";
import { CommentsProvider } from "@udecode/plate-comments";
import { Plate, createPlateEditor } from "@udecode/plate-common";
import { serializeHtml } from "@udecode/plate-serializer-html";
import { FC } from "react";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";

import { CommentsPopover } from "@/components/plate-ui/comments-popover";
import { Editor } from "@/components/plate-ui/editor";
import { FixedToolbar } from "@/components/plate-ui/fixed-toolbar";
import { FixedToolbarButtons } from "@/components/plate-ui/fixed-toolbar-buttons";
import { FloatingToolbar } from "@/components/plate-ui/floating-toolbar";
import { FloatingToolbarButtons } from "@/components/plate-ui/floating-toolbar-buttons";
import { MentionCombobox } from "@/components/plate-ui/mention-combobox";

interface PropsType {
  initialValue?: any;
}

const RichTextEditor: FC<PropsType> = ({ initialValue }) => {
  const editor = createPlateEditor({ plugins });

  const handleGetHtml = () => {
    // 点击时实时生成HTML
    const html = serializeHtml(editor, {
      nodes: editor.children,
      dndWrapper: (props) => <DndProvider backend={HTML5Backend} {...props} />,
    });
    console.log(html);
    // 这里可以把html传给父组件或者直接用于提交
  };

  return (
    <>
      <DndProvider backend={HTML5Backend}>
        <CommentsProvider users={{}} myUserId="1">
          <Plate
            plugins={plugins}
            initialValue={initialValue}
            editor={editor}
            onChange={() => {
              console.log("编辑器内容更新:", editor.children);
            }}
          >
            <FixedToolbar>
              <FixedToolbarButtons />
            </FixedToolbar>

            <Editor />

            <FloatingToolbar>
              <FloatingToolbarButtons />
            </FloatingToolbar>
            <MentionCombobox items={[]} />
            <CommentsPopover />
          </Plate>
        </CommentsProvider>
      </DndProvider>
      <button onClick={handleGetHtml}>获取HTML</button>
    </>
  );
};

export default RichTextEditor;

集成React-Hook-Form的版本

如果要把编辑器集成到React-Hook-Form中,需要使用Controller组件来管理表单值,将编辑器内容转为HTML后同步到表单:

"use client";

import { plugins } from "@/lib/plugins";
import { CommentsProvider } from "@udecode/plate-comments";
import { Plate, createPlateEditor } from "@udecode/plate-common";
import { serializeHtml } from "@udecode/plate-serializer-html";
import { FC } from "react";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import { Controller, useForm } from "react-hook-form";

import { CommentsPopover } from "@/components/plate-ui/comments-popover";
import { Editor } from "@/components/plate-ui/editor";
import { FixedToolbar } from "@/components/plate-ui/fixed-toolbar";
import { FixedToolbarButtons } from "@/components/plate-ui/fixed-toolbar-buttons";
import { FloatingToolbar } from "@/components/plate-ui/floating-toolbar";
import { FloatingToolbarButtons } from "@/components/plate-ui/floating-toolbar-buttons";
import { MentionCombobox } from "@/components/plate-ui/mention-combobox";

interface FormValues {
  content: string;
}

const RichTextEditorForm: FC = () => {
  const { control, handleSubmit } = useForm<FormValues>();

  const onSubmit = (data: FormValues) => {
    // 这里可以将data.content(HTML字符串)提交到数据库
    console.log("提交的HTML内容:", data.content);
    // 调用API存库逻辑
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Controller
        name="content"
        control={control}
        defaultValue=""
        render={({ field: { onChange } }) => {
          const editor = createPlateEditor({ plugins });

          // 编辑器内容变化时,同步生成HTML并更新表单值
          const handleEditorChange = () => {
            const html = serializeHtml(editor, {
              nodes: editor.children,
              dndWrapper: (props) => <DndProvider backend={HTML5Backend} {...props} />,
            });
            onChange(html);
          };

          return (
            <DndProvider backend={HTML5Backend}>
              <CommentsProvider users={{}} myUserId="1">
                <Plate
                  plugins={plugins}
                  initialValue={[]} // 可根据defaultValue反序列化,需补充deserializeHtml逻辑
                  editor={editor}
                  onChange={handleEditorChange}
                >
                  <FixedToolbar>
                    <FixedToolbarButtons />
                  </FixedToolbar>

                  <Editor />

                  <FloatingToolbar>
                    <FloatingToolbarButtons />
                  </FloatingToolbar>
                  <MentionCombobox items={[]} />
                  <CommentsPopover />
                </Plate>
              </CommentsProvider>
            </DndProvider>
          );
        }}
      />
      <button type="submit">提交表单</button>
    </form>
  );
};

export default RichTextEditorForm;

补充说明

  • 如果需要从HTML反序列化到编辑器初始值,可以使用deserializeHtml方法,对应serializeHtml的反向操作。
  • 确保你的plugins配置包含了所有需要序列化的节点插件,否则部分格式可能无法被正确转为HTML。

内容的提问来源于stack exchange,提问作者Eskalifer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 12:07:06