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

如何将Form组件中的textarea绑定到Tiptap编辑器?

问题:如何在Tiptap中复用表单的textarea元素而非使用content属性?

我是Tiptap的新手,希望使用FormComponent组件中的textarea元素,而非Tiptap编辑器配置项里的content属性,请问这是否可行?

当前尝试的代码如下:

TiptapEditor组件代码

const TiptapEditor = () => {
  const editor = useEditor({
    element: document.getElementById('description'), // 此方式无效
    extensions: [StarterKit],
    content: 'Hello World! 🌎️', // 我不想使用这个
  })

  return <EditorContent editor={editor} />
}

export default TiptapEditor

包含textarea的FormComponent组件代码

const FormComponent = () => {
  const {
    register,
    handleSubmit,
    watch,
    formState: { errors, isSubmitting, isSubmitted, isDirty, isValid },
  } = useForm<ExampleType>({
    mode: 'onChange',
    resolver: zodResolver(ExampleSchema),
    defaultValues: {
      description: '',
    },
  })
  return (
    <form onSubmit={handleSubmit(submit)} noValidate>
      <TiptapEditor /> // 目前无法正常工作
      <textarea
          id="description" // 我想使用这个textarea以避免重复样式
          placeholder={DESCRIPTION_PLACEHOLDER}
          {...register('description')}
        />
      <button></button>
    </form>
  )
}

我找不到相关示例或文档,请问有什么解决思路吗?


解决思路

可行,但不能直接用document.getElementById在组件初始化阶段获取DOM元素(此时textarea还未渲染完成),可以通过以下步骤实现:

  • 通过props传递textarea的ref:在FormComponent中创建textarea的ref,传递给TiptapEditor组件,避免直接操作DOM。
  • 同步Tiptap内容与表单字段:利用react-hook-form的watch监听字段变化,同步到Tiptap编辑器;同时监听Tiptap的内容变化,更新表单字段值。
  • 复用原生textarea样式:读取原生textarea的样式,应用到Tiptap编辑器容器,无需重复编写样式代码。

修改后的代码示例

FormComponent组件

import { useRef } from 'react';
import { useForm } from 'react-hook-form';
import TiptapEditor from './TiptapEditor';

const FormComponent = () => {
  const {
    register,
    handleSubmit,
    watch,
    setValue,
    formState: { errors, isSubmitting, isSubmitted, isDirty, isValid },
  } = useForm<ExampleType>({
    mode: 'onChange',
    resolver: zodResolver(ExampleSchema),
    defaultValues: {
      description: '',
    },
  });
  const descriptionRef = useRef(null);
  const descriptionValue = watch('description');

  return (
    <form onSubmit={handleSubmit(submit)} noValidate>
      <TiptapEditor 
        textareaRef={descriptionRef}
        initialContent={descriptionValue}
        onContentChange={(content) => setValue('description', content)}
      />
      <textarea
          ref={descriptionRef}
          id="description"
          placeholder={DESCRIPTION_PLACEHOLDER}
          style={{ display: 'none' }} // 隐藏原生textarea,保留字段注册状态
          {...register('description')}
        />
      <button type="submit">提交</button>
    </form>
  )
}

TiptapEditor组件

import { useEffect, useRef } from 'react';
import { useEditor, EditorContent } from '@tiptap/react';
import StarterKit from '@tiptap/starter-kit';

const TiptapEditor = ({ textareaRef, initialContent, onContentChange }) => {
  const editorRef = useRef(null);

  const editor = useEditor({
    extensions: [StarterKit],
    content: initialContent,
    onUpdate: ({ editor }) => {
      onContentChange(editor.getHTML()); // 编辑器内容变化时同步到表单字段
    },
    onCreate: ({ editor }) => {
      editorRef.current = editor;
      // 复制原生textarea的样式到编辑器容器
      if (textareaRef.current) {
        const editorContainer = editor.view.dom.parentNode;
        const textareaStyles = window.getComputedStyle(textareaRef.current);
        // 按需复制样式,比如字体、内边距、边框等
        editorContainer.style.fontSize = textareaStyles.fontSize;
        editorContainer.style.fontFamily = textareaStyles.fontFamily;
        editorContainer.style.padding = textareaStyles.padding;
        editorContainer.style.border = textareaStyles.border;
        editorContainer.style.borderRadius = textareaStyles.borderRadius;
      }
    },
  });

  // 监听表单字段变化,同步到编辑器
  useEffect(() => {
    if (editorRef.current && initialContent !== editorRef.current.getHTML()) {
      editorRef.current.commands.setContent(initialContent);
    }
  }, [initialContent]);

  return <EditorContent editor={editor} />
}

export default TiptapEditor;

关键说明

  1. 安全获取DOM元素:通过React的ref传递获取textarea,避免组件初始化阶段DOM未渲染完成的问题。
  2. 双向内容同步:实现编辑器与表单字段的实时同步,保证表单数据的一致性。
  3. 样式复用:通过getComputedStyle读取原生textarea的样式,直接应用到Tiptap编辑器,避免重复编写样式代码。
  4. 隐藏原生textarea:隐藏后仍保留其在表单中的注册状态,不影响表单提交逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 11:15:38