React中如何用Tiptap的setContent加载导出的HTML内容?
Tiptap 编辑器:Commands 位置说明与导出HTML重新加载方案
Commands 在哪里使用?
你代码里其实已经在大量使用Commands了——菜单栏每个按钮的onClick事件里,editor.chain().xxx().run()这种链式调用就是Commands的典型用法。比如toggleBold()、toggleItalic()、setParagraph()这些都是Tiptap内置的Commands,它们可以直接通过编辑器实例的chain()方法链式调用,用来执行各种编辑操作,位置就是你需要触发编辑动作的地方(比如按钮点击、页面加载后的初始化逻辑等)。
如何重新加载导出的HTML到编辑器?
要把之前导出的HTML重新加载,有两种常见场景:
1. 编辑器初始化时直接加载
把组件接收的newPostRichText作为编辑器的初始content,替换掉原来写死的示例内容。
2. 编辑器运行中动态更新内容
如果newPostRichText是在编辑器初始化后才更新的(比如从接口获取数据),可以用useEffect监听newPostRichText的变化,调用编辑器的setContent()方法动态设置内容。
下面是修改后的完整代码:
import "../../styles/tiptap.scss"; import { EditorContent, useEditor } from "@tiptap/react"; import StarterKit from "@tiptap/starter-kit"; import React, { useEffect } from "react"; const MenuBar = ({ editor }) => { if (!editor) { return null; } return ( <> <button onClick={() => editor.chain().focus().toggleBold().run()} disabled={!editor.can().chain().focus().toggleBold().run()} className={editor.isActive("bold") ? "is-active" : ""} > bold </button> <button onClick={() => editor.chain().focus().toggleItalic().run()} disabled={!editor.can().chain().focus().toggleItalic().run()} className={editor.isActive("italic") ? "is-active" : ""} > italic </button> <button onClick={() => editor.chain().focus().toggleStrike().run()} disabled={!editor.can().chain().focus().toggleStrike().run()} className={editor.isActive("strike") ? "is-active" : ""} > strike </button> <button onClick={() => editor.chain().focus().toggleCode().run()} disabled={!editor.can().chain().focus().toggleCode().run()} className={editor.isActive("code") ? "is-active" : ""} > code </button> <button onClick={() => editor.chain().focus().unsetAllMarks().run()}>clear marks</button> <button onClick={() => editor.chain().focus().clearNodes().run()}>clear nodes</button> <button onClick={() => editor.chain().focus().setParagraph().run()} className={editor.isActive("paragraph") ? "is-active" : ""} > paragraph </button> <button onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()} className={editor.isActive("heading", { level: 1 }) ? "is-active" : ""} > h1 </button> <button onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()} className={editor.isActive("heading", { level: 2 }) ? "is-active" : ""} > h2 </button> <button onClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()} className={editor.isActive("heading", { level: 3 }) ? "is-active" : ""} > h3 </button> <button onClick={() => editor.chain().focus().toggleHeading({ level: 4 }).run()} className={editor.isActive("heading", { level: 4 }) ? "is-active" : ""} > h4 </button> <button onClick={() => editor.chain().focus().toggleHeading({ level: 5 }).run()} className={editor.isActive("heading", { level: 5 }) ? "is-active" : ""} > h5 </button> <button onClick={() => editor.chain().focus().toggleHeading({ level: 6 }).run()} className={editor.isActive("heading", { level: 6 }) ? "is-active" : ""} > h6 </button> <button onClick={() => editor.chain().focus().toggleBulletList().run()} className={editor.isActive("bulletList") ? "is-active" : ""} > bullet list </button> <button onClick={() => editor.chain().focus().toggleOrderedList().run()} className={editor.isActive("orderedList") ? "is-active" : ""} > ordered list </button> <button onClick={() => editor.chain().focus().toggleCodeBlock().run()} className={editor.isActive("codeBlock") ? "is-active" : ""} > code block </button> <button onClick={() => editor.chain().focus().toggleBlockquote().run()} className={editor.isActive("blockquote") ? "is-active" : ""} > blockquote </button> <button onClick={() => editor.chain().focus().setHorizontalRule().run()}> horizontal rule </button> <button onClick={() => editor.chain().focus().setHardBreak().run()}>hard break</button> <button onClick={() => editor.chain().focus().undo().run()} disabled={!editor.can().chain().focus().undo().run()} > undo </button> <button onClick={() => editor.chain().focus().redo().run()} disabled={!editor.can().chain().focus().redo().run()} > redo </button> </> ); }; export default ({ newPostRichText, setNewPostRichText }) => { const editor = useEditor({ extensions: [StarterKit], // 初始化时优先使用传入的HTML,为空时用默认示例 content: newPostRichText || ` <h2> Hi there, </h2> <p> this is a <em>basic</em> example of <strong>tiptap</strong>. Sure, there are all kind of basic text styles you’d probably expect from a text editor. But wait until you see the lists: </p> <ul> <li> That’s a bullet list with one … </li> <li> … or two list items. </li> </ul> <p> Isn’t that great? And all of that is editable. But wait, there’s more. Let’s try a code block: </p> <pre><code class="language-css">body { display: none; }</code></pre> <p> I know, I know, this is impressive. It’s only the tip of the iceberg though. Give it a try and click a little bit around. Don’t forget to check the other examples too. </p> <blockquote> Wow, that’s amazing. Good work, boy! 👏 <br /> — Mom </blockquote> `, onUpdate: ({ editor }) => { setNewPostRichText(editor?.getHTML()); }, }); // 监听HTML内容变化,动态更新编辑器 useEffect(() => { if (editor && newPostRichText) { editor.setContent(newPostRichText); } }, [editor, newPostRichText]); return ( <div> <MenuBar editor={editor} /> <EditorContent editor={editor} /> </div> ); };
关键修改说明
- 初始化编辑器时,优先使用传入的
newPostRichText作为初始内容,避免默认示例覆盖已有数据 - 添加
useEffect监听newPostRichText的变化,一旦外部传入新的HTML,就调用editor.setContent()更新编辑器内容
内容的提问来源于stack exchange,提问作者James Hendrie
相关产品推荐
相关产品推荐

