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

React:收集子组件数组状态值并更新对象后保存到PouchDB

解决多Tiptap子组件统一保存到PouchDB的方案

子组件改造:用useImperativeHandle暴露数据接口

要在每个包含Tiptap Editor的子组件里,通过useImperativeHandle暴露获取编辑器JSON数据的方法,让父组件能直接调用:

import { useImperativeHandle, forwardRef, useEffect } from 'react';
import { useEditor, EditorContent } from '@tiptap/react';

const TableEditor = forwardRef(({ registerRef, unregisterRef, initialContent }, ref) => {
  const editor = useEditor({
    extensions: [], // 你的编辑器扩展配置
    content: initialContent || '',
  });

  // 暴露获取编辑器数据的方法
  useImperativeHandle(ref, () => ({
    getEditorData: () => editor?.getJSON() || {},
  }));

  // 挂载/卸载时维护父组件的ref数组
  useEffect(() => {
    registerRef(ref);
    return () => unregisterRef(ref);
  }, [registerRef, unregisterRef, ref]);

  return (
    <div className="table-editor">
      {editor && <EditorContent editor={editor} />}
    </div>
  );
});

TableEditor.displayName = 'TableEditor';
export default TableEditor;

父组件:维护数组Ref并实现统一保存

父组件用useRef创建数组存储所有子组件引用,点击统一保存按钮时遍历数组收集数据,再处理PouchDB的保存逻辑:

import { useRef } from 'react';
import TableEditor from './TableEditor';
import PouchDB from 'pouchdb';

const db = new PouchDB('your-database-name');

const ParentComponent = () => {
  const editorRefs = useRef([]);

  // 注册子组件ref
  const registerEditorRef = (ref) => {
    if (ref && !editorRefs.current.includes(ref)) {
      editorRefs.current.push(ref);
    }
  };

  // 注销子组件ref(防止内存泄漏)
  const unregisterEditorRef = (ref) => {
    editorRefs.current = editorRefs.current.filter(item => item !== ref);
  };

  // 统一保存逻辑
  const handleSaveAll = async () => {
    // 收集所有子编辑器的数据
    const sections = editorRefs.current.map(ref => ref.getEditorData());

    try {
      // 先获取目标文档的最新版本(不存在则创建新文档)
      let targetDoc;
      try {
        targetDoc = await db.get('your-document-id');
      } catch (err) {
        targetDoc = { _id: 'your-document-id', sections: [] };
      }

      // 更新sections数组(按需替换或合并)
      targetDoc.sections = sections;

      // 写入PouchDB
      await db.put(targetDoc);
      console.log('所有表格数据保存成功');
    } catch (err) {
      console.error('保存失败:', err);
    }
  };

  return (
    <div>
      {/* 渲染多个子编辑器组件 */}
      <TableEditor
        initialContent="<table>...</table>"
        registerRef={registerEditorRef}
        unregisterRef={unregisterEditorRef}
      />
      <TableEditor
        initialContent="<table>...</table>"
        registerRef={registerEditorRef}
        unregisterRef={unregisterEditorRef}
      />

      {/* 统一保存按钮 */}
      <button onClick={handleSaveAll}>保存所有表格</button>
    </div>
  );
};

export default ParentComponent;

关键注意点

  • 数组Ref维护:通过子组件的useEffect完成注册与注销,确保父组件的ref数组始终与当前挂载的子组件同步,避免内存泄漏。
  • PouchDB更新逻辑:必须先获取文档的最新版本再执行put操作,否则会出现仅更新_rev但实际数据未写入的情况,这也是你之前遇到问题的核心原因。
  • 数据合并策略:如果不需要完全替换sections数组,可以给每个子组件添加唯一标识(如id或index),对应更新数组中的指定项,避免覆盖原有数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 20:01:44