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

Formik自定义字段传入error、touched props时输入失焦问题

Formik自定义字段传入error、touched后输入失焦问题解决方案

问题根因

  • 最常见的原因是你在父组件的渲染函数作用域内直接声明了自定义输入组件:当你传入error、touched属性时,每次输入都会触发Formik状态更新(字段值、touched状态、校验错误信息任意一项变化),进而触发父组件重渲染,此时你在函数体内定义的自定义组件会被视为全新的组件类型,React会卸载旧的输入DOM节点、挂载新的节点,自然就会出现输入后失焦的问题。
  • 移除error、touched后Formik不会在输入时触发额外的状态更新,重渲染逻辑没有触发,因此不会复现失焦问题。
  • 少数情况下也可能是你给自定义字段组件绑定了动态变化的key属性,每次重渲染key值改变导致React重建组件节点。

修复方案

方案1:将自定义字段组件移到父组件外部定义(最推荐)

把自定义输入组件的定义从父组件的函数体里挪到外层作用域,避免每次重渲染生成新的组件类型。

// 外层作用域定义自定义组件,仅初始化生成一次
const CustomInput = ({ field, form: { touched, errors } }) => (
  <div>
    <input type="text" {...field} />
    {touched[field.name] && errors[field.name] && <span className="error-tip">{errors[field.name]}</span>}
  </div>
)

// 父组件内直接调用
function UserForm() {
  return (
    <Formik
      initialValues={{ username: '' }}
      validationSchema={userSchema}
      onSubmit={handleSubmit}
    >
      <Form>
        <Field name="username" component={CustomInput} />
        <button type="submit">提交</button>
      </Form>
    </Formik>
  )
}

方案2:使用useCallback缓存组件定义

如果确实需要在父组件内部定义自定义组件,可以用useCallback缓存组件实例,依赖项留空即可保证重渲染时不会生成新的引用。

function UserForm() {
  // 用useCallback缓存组件定义
  const CustomInput = useCallback(({ field, form: { touched, errors } }) => (
    <div>
      <input type="text" {...field} />
      {touched[field.name] && errors[field.name] && <span className="error-tip">{errors[field.name]}</span>}
    </div>
  ), [])

  return (
    <Formik
      initialValues={{ username: '' }}
      validationSchema={userSchema}
      onSubmit={handleSubmit}
    >
      <Form>
        <Field name="username" component={CustomInput} />
        <button type="submit">提交</button>
      </Form>
    </Formik>
  )
}

方案3:使用render prop写法替代component属性

直接使用<Field>内置的render属性传入渲染函数,不会触发组件的卸载重建逻辑,写法更灵活。

function UserForm() {
  return (
    <Formik
      initialValues={{ username: '' }}
      validationSchema={userSchema}
      onSubmit={handleSubmit}
    >
      <Form>
        <Field name="username">
          {({ field, form: { touched, errors } }) => (
            <div>
              <input type="text" {...field} />
              {touched[field.name] && errors[field.name] && <span className="error-tip">{errors[field.name]}</span>}
            </div>
          )}
        </Field>
        <button type="submit">提交</button>
      </Form>
    </Formik>
  )
}

内容的提问来源于stack exchange,提问作者Rishabh Sehgal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.08 11:57:03