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} 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
相关产品推荐
相关产品推荐

