MUI富文本编辑器在Formik表单中光标跳转至开头输入反向问题
解决MUI Rich Text Editor在Formik表单中光标始终在开头的问题
问题原因
- 非受控模式误用:编辑器使用
defaultValue而非受控的value属性,Formik表单值变化时编辑器无法同步,每次输入触发组件重渲染后光标被重置。 - 内部状态冗余:组件维护的
initialValue状态,在输入时更新会强制编辑器重新渲染,导致光标回到内容开头。 - 值同步失效:Formik的
values.description变化时,编辑器未及时同步最新值,始终依赖初始的defaultValue。
解决方案
- 切换为受控组件模式:用
value属性绑定转换后的Formik表单值,替代defaultValue。 - 移除内部冗余状态:删除
initialValue相关的useState,直接通过props的value计算编辑器所需格式。 - 优化值转换逻辑:将值转换逻辑改为纯函数,用
useMemo缓存结果避免重复计算。 - 避免不必要重渲染:确保编辑器仅在内容实际变化时更新,防止光标频繁重置。
修改后的Editor组件代码
function Editor({ onFocus, onChange, value, name, height, disabled, label, enlarge, setFieldValue, html, }: { onFocus?: () => void; onChange?: (value: string | any) => void; setFieldValue: (name: string, value: string | any) => void; html?: boolean; enlarge?: boolean; disabled?: boolean; value: any; name: string; height?: number | string; label?: string; }) { const editorRef: any = useRef(null); // 转换Formik值为编辑器所需的ContentState格式,用useMemo缓存结果 const getEditorValue = useMemo(() => { if (value !== undefined && value !== null) { // 兼容JSON字符串和纯文本两种输入格式 let contentState; try { const parsedValue = JSON.parse(value); contentState = convertFromRaw(parsedValue); } catch (e) { contentState = ContentState.createFromText(value); } return convertToRaw(contentState); } return convertToRaw(ContentState.createFromText('')); }, [value]); const performChange = async (newValue: any) => { if (onChange && value !== newValue) { onChange(newValue); } if (setFieldValue && value !== newValue) { setFieldValue(name, newValue); } }; const handleChange = (event: any) => { if (html === true) { const newValueHTML = stateToHTML(event.getCurrentContent()); performChange(newValueHTML); } else { const newValue = JSON.stringify(convertToRaw(event.getCurrentContent())); performChange(newValue); } }; return ( <> <ThemeProvider theme={defaultTheme}> <MUIRichTextEditor ref={editorRef} onFocus={onFocus} value={getEditorValue} // 使用受控的value属性绑定内容 onChange={handleChange} inlineToolbar={!disabled} label={disabled ? '' : label} toolbarButtonSize="small" toolbar={!disabled} readOnly={disabled} /> </ThemeProvider> </> ); }
修改后的Formik表单代码
<Formik> ({ isValid, handleSubmit, dirty, values, setFieldValue, setFieldTouched, touched }: any) => ( <Form> <Editor onFocus={() => { if (values.description !== undefined) { setFieldTouched('description', true, true); } }} setFieldValue={setFieldValue} height={150} value={values.description} name="description" enlarge={true} disabled={values.disabled === true} label="描述" /> {/* 其他表单元素 */} </Form> ) </Formik>
关键说明
- 受控绑定:通过
value={getEditorValue}让编辑器与Formik值实时同步,避免光标因组件重渲染被重置。 - 缓存优化:
useMemo确保仅在value变化时重新计算编辑器内容,减少不必要的性能消耗。 - 格式兼容:增加JSON解析容错,同时支持纯文本和JSON格式的输入值。
内容的提问来源于stack exchange,提问作者Abhinav Soni
相关产品推荐
相关产品推荐

