React Quill编辑器onChange触发时失焦问题求助
React Quill onChange触发后失焦问题解决方案
问题根源分析
你的代码存在两个核心问题导致失焦:
- 内部组件重复创建:
Editor组件定义在EditorFirstComp内部,每次父组件因fieldList或content变化重渲染时,这个内部组件都会被重新定义,导致ReactQuill实例被销毁重建,直接丢失焦点。 - 错误使用defaultValue:ReactQuill作为受控组件时,应该用
value属性而非defaultValue,defaultValue仅用于初始化,后续content变化不会同步到编辑器,反而会引发不必要的重渲染逻辑。
另外你尝试的useRef直接focus会定位到开头,是因为没有保存之前的光标位置,恢复时直接重置到起始点。
修复步骤及代码示例
1. 把内部Editor组件移到外部
将Editor组件从EditorFirstComp内部提取出来,避免每次父组件重渲染都重新创建组件。
2. 改用value属性绑定content
将ReactQuill的defaultValue替换为value,确保编辑器内容和props的content同步。
3. 保存光标位置并恢复焦点
使用useRef保存Quill实例和光标位置,在content更新后恢复到之前的位置。
完整修复后的代码:
import React, { useRef, useEffect, useState } from "react"; import { connect } from "react-redux"; import ReactQuill, { Quill } from "react-quill"; import "quill-mention"; import "react-quill/dist/quill.snow.css"; // 把Editor组件移到外部,避免父组件重渲染时重复创建 const Editor = ({ setContent, content, modules }) => { const quillRef = useRef(null); const [lastSelection, setLastSelection] = useState(null); // 保存光标位置 useEffect(() => { const quill = quillRef.current?.getEditor(); if (quill) { quill.on("selection-change", (range) => { if (range) { setLastSelection(range); } }); } return () => { const quill = quillRef.current?.getEditor(); if (quill) { quill.off("selection-change"); } }; }, []); // 内容更新后恢复光标位置 useEffect(() => { const quill = quillRef.current?.getEditor(); if (quill && lastSelection) { // 延迟执行确保内容已更新 setTimeout(() => { quill.setSelection(lastSelection); }, 0); } }, [content]); const handleChange = (value) => { setContent(value); }; return ( <form> <ReactQuill ref={quillRef} theme="snow" value={content} // 改用value绑定受控内容 onChange={handleChange} modules={modules} /> </form> ); }; const EditorFirstComp = ({ setContent, content, fieldList, setDataFromEditor, }) => { const atValues = fieldList.map( (field) => ({ id: field.key, value: field.label }) ); const hashValues = [ { id: 3, value: "Fredrik Sundqvist 2" }, { id: 4, value: "Patrik Sjölin 2" }, ]; const mentionModuleConfig = { allowedChars: /^[A-Za-z\sÅÄÖåäö]*$/, mentionDenotationChars: ["@", "#"], source: function (searchTerm, renderList, mentionChar) { let values = mentionChar === "@" ? atValues : hashValues; if (searchTerm.length === 0) { renderList(values, searchTerm); } else { const matches = values.filter(item => item.value.toLowerCase().includes(searchTerm.toLowerCase()) ); renderList(matches, searchTerm); } }, }; const modules = { mention: mentionModuleConfig, }; return ( <Editor setContent={setContent} content={content} modules={modules} /> ); }; const mapStateToProps = (state) => ({ fieldList: state.field, }); const mapDispatchToProps = (dispatch) => ({}); export default connect(mapStateToProps, mapDispatchToProps)(EditorFirstComp);
额外优化点
- 简化了
atValues的生成逻辑,去掉了多余的数组包裹 - 用
filter替代了原始的for循环,代码更简洁易读 - 给Quill的事件监听添加了清理函数,避免内存泄漏
内容的提问来源于stack exchange,提问作者Nurzhan Abayev
相关产品推荐
相关产品推荐

