如何在React的Draft.js中实现自定义快捷文本格式功能?
解决Draft.js的Markdown风格快捷输入需求
下面是针对你提出的四个快捷输入需求的完整实现代码,替换现有代码即可直接使用:
import React, { useState, useCallback } from 'react'; import { Editor, EditorState, RichUtils, getDefaultKeyBinding, Modifier, SelectionState } from 'draft-js'; import 'draft-js/dist/Draft.css'; export default function App() { const [editorState, setEditorState] = useState(EditorState.createEmpty()); // 获取光标所在行的前缀文本 const getCurrentLinePrefix = (editorState) => { const selection = editorState.getSelection(); const contentState = editorState.getCurrentContent(); const block = contentState.getBlockForKey(selection.getStartKey()); return block.getText().slice(0, selection.getStartOffset()).trim(); }; const keyBindingFn = useCallback((event) => { if (event.keyCode === 32) { // 监听空格按键触发快捷规则 return "apply-markdown-shortcut"; } return getDefaultKeyBinding(event); }, []); const handleKeyCommand = useCallback( (command) => { if (command === "apply-markdown-shortcut") { const prefix = getCurrentLinePrefix(editorState); const contentState = editorState.getCurrentContent(); const selection = editorState.getSelection(); const blockKey = selection.getStartKey(); let newEditorState = editorState; let removeLength = 0; // 需求1:# + 空格 → 转为一级标题并删除# if (prefix === '#') { removeLength = 2; // # + 空格的总长度 // 删除前缀字符 const updatedContent = Modifier.replaceText( contentState, SelectionState.createEmpty(blockKey).merge({ anchorOffset: 0, focusOffset: removeLength }), '' ); newEditorState = EditorState.push(editorState, updatedContent, 'remove-range'); // 应用标题格式 newEditorState = RichUtils.toggleBlockType(newEditorState, 'header-one'); setEditorState(newEditorState); return "handled"; } // 需求2:* + 空格 → 转为粗体并删除* if (prefix === '*') { removeLength = 2; const updatedContent = Modifier.replaceText( contentState, SelectionState.createEmpty(blockKey).merge({ anchorOffset: 0, focusOffset: removeLength }), '' ); newEditorState = EditorState.push(editorState, updatedContent, 'remove-range'); // 应用粗体样式 newEditorState = RichUtils.toggleInlineStyle(newEditorState, 'BOLD'); setEditorState(newEditorState); return "handled"; } // 需求3:** + 空格 → 红色下划线并删除** if (prefix === '**') { removeLength = 3; const updatedContent = Modifier.replaceText( contentState, SelectionState.createEmpty(blockKey).merge({ anchorOffset: 0, focusOffset: removeLength }), '' ); newEditorState = EditorState.push(editorState, updatedContent, 'remove-range'); // 应用自定义红色下划线样式 newEditorState = RichUtils.toggleInlineStyle(newEditorState, 'RED_UNDERLINE'); setEditorState(newEditorState); return "handled"; } // 需求4:*** + 空格 → 下划线并删除*** if (prefix === '***') { removeLength = 4; const updatedContent = Modifier.replaceText( contentState, SelectionState.createEmpty(blockKey).merge({ anchorOffset: 0, focusOffset: removeLength }), '' ); newEditorState = EditorState.push(editorState, updatedContent, 'remove-range'); // 应用下划线样式 newEditorState = RichUtils.toggleInlineStyle(newEditorState, 'UNDERLINE'); setEditorState(newEditorState); return "handled"; } return "not-handled"; } return "not-handled"; }, [editorState] ); // 自定义红色下划线样式映射 const customStyleMap = { RED_UNDERLINE: { textDecoration: 'underline', textDecorationColor: 'red', }, }; return ( <div style={{ border: "2px solid black", padding: '10px' }}> <Editor editorState={editorState} onChange={setEditorState} handleKeyCommand={handleKeyCommand} keyBindingFn={keyBindingFn} customStyleMap={customStyleMap} /> </div> ); }
核心实现细节:
- 前缀检测:通过
getCurrentLinePrefix获取光标所在行的纯文本前缀,判断是否匹配快捷规则 - 精准删除:使用
Modifier.replaceText删除行首的符号及后续空格,确保无无效字符残留 - 格式应用:
- 标题用
RichUtils.toggleBlockType设置块级格式 - 粗体、下划线用
RichUtils.toggleInlineStyle设置行内样式,红色下划线通过customStyleMap自定义实现
- 标题用
- 光标自动校准:删除前缀后自动调整光标位置,保证输入流程顺畅
内容的提问来源于stack exchange,提问作者nidhi sharma
相关产品推荐
相关产品推荐

