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

React-Admin中为RichTextInput添加字符计数的实现问题

解决React-Admin RichTextInput集成Tiptap CharacterCount无计数结果的问题

问题描述

尝试使用Tiptap的@tiptap/extension-character-count扩展为React-Admin的RichTextInput添加字符计数功能,但输入时无法获取到计数结果。

问题代码

import {
  RichTextInput,
  DefaultEditorOptions,
  useTiptapEditor,
} from 'ra-input-rich-text';
import CharacterCount from '@tiptap/extension-character-count';
import { ToggleButton } from '@mui/material';

export default function RichEditor(props) {
  const { limit, ...rest } = props;
  const editorOptions = {
    ...DefaultEditorOptions,
    extensions: [
      ...DefaultEditorOptions.extensions,
      CharacterCount.configure({
        mode: 'nodeSize',
        limit,
      }),
    ],
  };
  const testEditor = useTiptapEditor();

  return (
    <RichTextInput
      editorOptions={editorOptions}
      helperText={( 
        <span className="character-count">
          {testEditor?.storage?.characterCount?.characters()}
          /
          {limit}
          &nbsp;characters
          <br />
          {testEditor?.storage?.characterCount?.words()}
          words
        </span>
      )}
      {...rest}
    />
  );
}

核心问题

你直接调用useTiptapEditor()获取的实例,和RichTextInput内部创建的编辑器实例不是同一个。ra-input-rich-text的useTiptapEditor是供内部组件使用的,外部调用无法拿到正确的编辑器实例,自然获取不到characterCount的计数结果。

解决方法

方法1:通过editorProps监听编辑器实例并更新计数

利用RichTextInput的editorProps属性,在编辑器初始化和内容变化时获取实例,通过状态维护计数:

import { useState, useCallback } from 'react';
import {
  RichTextInput,
  DefaultEditorOptions,
} from 'ra-input-rich-text';
import CharacterCount from '@tiptap/extension-character-count';

export default function RichEditor(props) {
  const { limit, ...rest } = props;
  const [characterCount, setCharacterCount] = useState(0);
  const [wordCount, setWordCount] = useState(0);

  const editorOptions = {
    ...DefaultEditorOptions,
    extensions: [
      ...DefaultEditorOptions.extensions,
      CharacterCount.configure({
        mode: 'nodeSize',
        limit,
      }),
    ],
  };

  const handleEditorInit = useCallback((editor) => {
    // 初始化时获取计数
    updateCounts(editor);
    // 监听内容变化,实时更新计数
    editor.on('update', () => updateCounts(editor));
  }, []);

  const updateCounts = useCallback((editor) => {
    if (editor.storage.characterCount) {
      setCharacterCount(editor.storage.characterCount.characters());
      setWordCount(editor.storage.characterCount.words());
    }
  }, []);

  return (
    <RichTextInput
      editorOptions={editorOptions}
      editorProps={{
        onInit: handleEditorInit,
      }}
      helperText={( 
        <span className="character-count">
          {characterCount} / {limit} characters
          <br />
          {wordCount} words
        </span>
      )}
      {...rest}
    />
  );
}

方法2:自定义组件直接绑定编辑器实例

如果需要更灵活的控制,可以基于底层组件自定义,直接拿到编辑器实例:

import { useState, useCallback, useEffect } from 'react';
import {
  RichTextInput as BaseRichTextInput,
  DefaultEditorOptions,
  useTiptapEditor,
} from 'ra-input-rich-text';
import CharacterCount from '@tiptap/extension-character-count';

export default function RichEditor(props) {
  const { limit, ...rest } = props;
  const [characterCount, setCharacterCount] = useState(0);
  const [wordCount, setWordCount] = useState(0);

  const editorOptions = {
    ...DefaultEditorOptions,
    extensions: [
      ...DefaultEditorOptions.extensions,
      CharacterCount.configure({
        mode: 'nodeSize',
        limit,
      }),
    ],
  };

  // 这里的editor是当前组件内部的实例,与BaseRichTextInput共享
  const editor = useTiptapEditor({
    editorOptions,
    content: props.value,
    onUpdate: ({ editor }) => {
      props.onChange(editor.getHTML());
      updateCounts(editor);
    },
  });

  const updateCounts = useCallback((editor) => {
    if (editor.storage.characterCount) {
      setCharacterCount(editor.storage.characterCount.characters());
      setWordCount(editor.storage.characterCount.words());
    }
  }, []);

  // 初始化时更新计数
  useEffect(() => {
    if (editor) updateCounts(editor);
  }, [editor]);

  return (
    <BaseRichTextInput
      editor={editor}
      helperText={( 
        <span className="character-count">
          {characterCount} / {limit} characters
          <br />
          {wordCount} words
        </span>
      )}
      {...rest}
    />
  );
}

额外注意点

  • 如果需要统计纯文本字符数而非HTML节点大小,将mode: 'nodeSize'改为mode: 'textSize'。
  • 确保@tiptap/extension-character-count的版本与ra-input-rich-text依赖的Tiptap版本一致,避免版本冲突导致功能异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 20:33:21