---
title: "在 React 组合式 API 中使用 Tiptap"
description: "了解如何将 Tiptap 的可组合 React API 与声明式 Tiptap 组件结合使用，实现更简洁的集成体验。"
canonical_url: "https://tiptap.zhcndoc.com/guides/react-composable-api"
---

# 在 React 组合式 API 中使用 Tiptap

了解如何将 Tiptap 的可组合 React API 与声明式 Tiptap 组件结合使用，实现更简洁的集成体验。

Tiptap 提供了一个声明式的 `<Tiptap>` 组件，简化了编辑器的设置，并自动为所有子组件提供上下文。该组合式 API 是基于钩子的 `useEditor` + `<EditorContent />` 方式的替代方案，提供了更符合 React 习惯的 Tiptap 使用方式。

## 何时使用组合式 API

当你满足以下需求时，组合式 API 是理想选择：

- 需要一种更具声明式、基于组件的方式
- 需要从多个子组件中访问编辑器
- 希望自动进行上下文管理，而不是手动传递 props
- 正在构建复杂的编辑器 UI（工具栏、菜单、侧边栏、区块）

对于更简单的用例或需要更直接控制时，[基于钩子的 useEditor 方式](https://tiptap.zhcndoc.com/editor/getting-started/install/react.md) 可能更合适。

## 安装

在使用组合式 API 之前，请确保在你的 React 项目中已安装 Tiptap。请按照 [React 安装指南](https://tiptap.zhcndoc.com/editor/getting-started/install/react.md) 设置所需依赖。

## 使用 Tiptap 组件

`<Tiptap>` 组件是根提供者，通过 React 上下文将编辑器实例提供给所有子组件。

### 基本设置

创建一个新的 React 组件，并从 `@tiptap/react` 中导入 `Tiptap` 组件以及 `useEditor`（菜单组件从 `@tiptap/react/menus` 导入）：

```tsx
// 源码/Editor.tsx
"use client"

import { Tiptap, useEditor } from '@tiptap/react'
import { BubbleMenu, FloatingMenu } from '@tiptap/react/menus'
import StarterKit from '@tiptap/starter-kit'

function Editor() {
  const editor = useEditor({
    extensions: [StarterKit],
    content: '<p>Hello World!</p>',
  })

  if (!editor) return null

  return (
    <Tiptap editor={editor}>
      <Tiptap.Content />

      <BubbleMenu editor={editor}>
        <button>Bold</button>
        <button>Italic</button>
      </BubbleMenu>

      <FloatingMenu editor={editor}>
        <button>Add heading</button>
      </FloatingMenu>
    </Tiptap>
  )
}

export default Editor
```

### 可用子组件

| 组件               | 描述                                                |
| ---------------- | ------------------------------------------------- |
| `Tiptap.Content` | 渲染编辑器内容区域。替代 `<EditorContent editor={editor} />`。 |

菜单组件需要从 `@tiptap/react/menus` 中单独导入。

## 在子组件中访问编辑器

组合式 API 的主要优势之一是子组件可以无需传递 props 直接访问编辑器实例。

### 使用 useTiptap 钩子

`useTiptap` 钩子会从上下文中返回编辑器实例。

```tsx
import { useTiptap } from '@tiptap/react'

function MenuBar() {
  const { editor } = useTiptap()

  if (!editor) return null

  return (
    <div className="menu-bar">
      <button
        onClick={() => editor.chain().focus().toggleBold().run()}
        className={editor.isActive('bold') ? 'is-active' : ''}
      >
        加粗
      </button>
      <button
        onClick={() => editor.chain().focus().toggleItalic().run()}
        className={editor.isActive('italic') ? 'is-active' : ''}
      >
        斜体
      </button>
    </div>
  )
}
```

然后在你的 `<Tiptap>` 组件内的任意位置使用该菜单栏：

```tsx
<Tiptap editor={editor}>
  <MenuBar />
  <Tiptap.Content />
</Tiptap>
```

### 使用 useTiptapState 订阅响应式状态

针对性能敏感的组件，可使用 `useTiptapState` 订阅编辑器状态的特定部分。这样能避免无关状态变化导致的不必要重新渲染。

```tsx
import { useTiptapState } from '@tiptap/react'

function WordCount() {
  const { editor } = useTiptap()

  const wordCount = useTiptapState((state) => {
    const text = state.editor.state.doc.textContent
    return text.split(/\s+/).filter(Boolean).length
  })

  if (!editor) {
    return null
  }

  return <span>{wordCount} 字</span>
}
```

选择器函数接收一个 `EditorStateSnapshot`，应返回组件需要的数据。只有当选中值发生变化时，组件才会重新渲染。

## 服务端渲染（SSR）

组合式 API 可与服务端渲染无缝协作。由于编辑器实例只会在客户端创建，因此你可以在它存在之前先阻止渲染：

```tsx
"use client"

import { Tiptap, useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'

export function MyEditor() {
  const editor = useEditor({
    extensions: [StarterKit],
    content: '<p>Hello World!</p>',
  })

  if (!editor) {
    return <div className="skeleton">Loading editor...</div>
  }

  return (
    <Tiptap editor={editor}>
      <Tiptap.Content />
    </Tiptap>
  )
}
```

## 性能注意事项

组合式 API 在设计时注重性能：

- **自动上下文优化**：编辑器上下文会被缓存，以防止不必要的重新渲染
- **选择性订阅**：使用 `useTiptapState` 只订阅你需要的状态

更多性能技巧见[React 性能指南](https://tiptap.zhcndoc.com/guides/performance.md)。

## 向后兼容性

`<Tiptap>` 组件自动提供了 `EditorContext`，因此可以在其内部使用 `useCurrentEditor` 钩子，以兼容现有代码：

```tsx
import { useCurrentEditor } from '@tiptap/react'

function EditorJSONPreview() {
  const { editor } = useCurrentEditor()

  if (!editor) return null

  return <pre>{JSON.stringify(editor.getJSON(), null, 2)}</pre>
}
```

不过，对于新代码，我们建议使用 `useTiptap()`。

## API 参考

### Tiptap 组件

根提供者组件，通过 React 上下文提供编辑器实例。

**Props：**

| 属性         | 类型               | 说明                      |
| ---------- | ---------------- | ----------------------- |
| `editor`   | `Editor \| null` | 来自 `useEditor()` 的编辑器实例 |
| `children` | `ReactNode`      | 子组件                     |

**示例：**

```tsx
<Tiptap editor={editor}>
  <Tiptap.Content />
</Tiptap>
```

### useTiptap 钩子

返回 Tiptap 上下文值。

**返回值：**

| 属性       | 类型               | 说明    |
| -------- | ---------------- | ----- |
| `editor` | `Editor \| null` | 编辑器实例 |

**示例：**

```tsx
const { editor } = useTiptap()

if (!editor) return null
```

### useTiptapState 钩子

通过选择器函数订阅编辑器状态的部分片段。

**签名：**

```tsx
const value = useTiptapState(selector, equalityFn?)
```

**参数：**

| 参数           | 类型                                  | 说明                |
| ------------ | ----------------------------------- | ----------------- |
| `selector`   | `(state: EditorStateSnapshot) => T` | 用于选择状态的函数         |
| `equalityFn` | `(a: T, b: T) => boolean`           | 可选的相等性函数，用于控制重新渲染 |

**示例：**

```tsx
const isBold = useTiptapState((state) =>
  state.editor.isActive('bold')
)
```

## 对比：组合式 API 与基于钩子的方式

| 特性     | 组合式 API                | 基于钩子的方式                          |
| ------ | ---------------------- | -------------------------------- |
| 设置复杂度  | 低 – 声明式组件              | 中 – 手动传递 props                   |
| 上下文管理  | 自动                     | 通过 `EditorContext.Provider` 手动管理 |
| 子组件访问  | 通过 `useTiptap()` 轻松访问  | 需要逐层传递 props 或使用 context         |
| SSR 支持 | 受保护的渲染（`if (!editor)`） | 手动进行 null 检查                     |
| 性能     | 使用 `useTiptapState` 优化 | 使用 `useEditorState` 优化           |
| 最适合    | 拥有许多子组件的复杂 UI          | 简单 UI 或需要直接控制时                   |

## 下一步

- [优化你的 React 集成](https://tiptap.zhcndoc.com/guides/performance.md)
- [配置你的编辑器](https://tiptap.zhcndoc.com/editor/getting-started/configure.md)
- [为编辑器添加样式](https://tiptap.zhcndoc.com/editor/getting-started/style-editor.md)
- [了解更多 Tiptap 概念](https://tiptap.zhcndoc.com/editor/core-concepts/introduction.md)
