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

MUI富文本编辑器在Formik表单中光标跳转至开头输入反向问题

解决MUI Rich Text Editor在Formik表单中光标始终在开头的问题

问题原因

  1. 非受控模式误用:编辑器使用defaultValue而非受控的value属性,Formik表单值变化时编辑器无法同步,每次输入触发组件重渲染后光标被重置。
  2. 内部状态冗余:组件维护的initialValue状态,在输入时更新会强制编辑器重新渲染,导致光标回到内容开头。
  3. 值同步失效:Formik的values.description变化时,编辑器未及时同步最新值,始终依赖初始的defaultValue。

解决方案

  1. 切换为受控组件模式:用value属性绑定转换后的Formik表单值,替代defaultValue。
  2. 移除内部冗余状态:删除initialValue相关的useState,直接通过props的value计算编辑器所需格式。
  3. 优化值转换逻辑:将值转换逻辑改为纯函数,用useMemo缓存结果避免重复计算。
  4. 避免不必要重渲染:确保编辑器仅在内容实际变化时更新,防止光标频繁重置。

修改后的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 15:59:52