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

使用Yup + Formik验证对象数组时的触发表单样式问题

问题

我正在使用Yup + Formik验证对象数组,已编写的Yup校验规则如下:

Yup.object().shape({
  Enderecos: Yup.array()
    .of(
      Yup.object().shape({
        BairroId: Yup.number()
          .required('Campo obrigatório'),
        CtbEndCadMunicipio: Yup.number()
          .required('Campo obrigatório'),
        CtbEndTipo: Yup.number()
          .required('Campo obrigatório'),
        LograId: Yup.number()
          .required('Campo obrigatório'),
        MunId: Yup.number(),
        CtbEndComplemento: Yup.string()
          .min(defaultMinimumCharacters, `Minimo de ${defaultMinimumCharacters} caracteres`)
          .max(200, `Máximo de ${200} caracteres`)
          .required('Campo obrigatório'),
        CtbEndNumero: Yup.string()
          .min(defaultMinimumCharacters, `Minimo de ${defaultMinimumCharacters} caracteres`)
          .max(10, `Máximo de ${10} caracteres`)
          .required('Campo obrigatório'),
        CtbEndCep: Yup.string()
          .min(defaultMinimumCharacters, `Minimo de ${defaultMinimumCharacters} caracteres`)
          .max(8, `Máximo de ${8} caracteres`)
          .required('Campo obrigatório'),
      })
    )}),

同时基于Formik编写的表单字段代码如下:

<div className='fv-row me-4 mb-7 w-100'>
  <label className='required fw-bold fs-6 mb-2'>CEP</label>
  <input
    placeholder='CEP'
    {...values?.getFieldProps('Enderecos[0].CtbEndCep')}
    type='text'
    maxLength={8}
    name='Enderecos[0].CtbEndCep'
    className={clsx(
      'form-control form-control-solid mb-3 mb-lg-0',
      { 'is-invalid': values?.touched.CtbEndCep && values?.errors.CtbEndCep },
      {
        'is-valid': values?.touched.CtbEndCep && !values?.errors.CtbEndCep,
      }
    )}
    onChange={(e: React.ChangeEvent<HTMLInputElement>) => values?.setFieldValue('Enderecos[0].CtbEndCep', e.target.value)}
  />
  {values?.touched.CtbEndCep && values?.errors.CtbEndCep && (
    <div className='fv-plugins-message-container'>
      <div className='fv-help-block'>
        <span role='alert'>{values?.errors.CtbEndCep}</span>
      </div>
    </div>
  )}
</div>

问题:尝试访问数组内字段的触发表单状态(如values?.touched.Enderecos[0].CtbEndCep)时,表单首次渲染时touched对象为空,会出现undefined错误,导致无法正常显示表单验证的CSS样式(字段填写正确时显示绿色,错误时显示红色)。


解决方法

1. 用可选链操作符安全访问嵌套属性

直接在访问touched和errors的嵌套属性时添加可选链,避免空值导致的报错。修改后的逻辑如下:

className={clsx(
  'form-control form-control-solid mb-3 mb-lg-0',
  { 'is-invalid': values?.touched?.Enderecos?.[0]?.CtbEndCep && values?.errors?.Enderecos?.[0]?.CtbEndCep },
  { 'is-valid': values?.touched?.Enderecos?.[0]?.CtbEndCep && !values?.errors?.Enderecos?.[0]?.CtbEndCep }
)}

错误提示部分:

{values?.touched?.Enderecos?.[0]?.CtbEndCep && values?.errors?.Enderecos?.[0]?.CtbEndCep && (
  <div className='fv-plugins-message-container'>
    <div className='fv-help-block'>
      <span role='alert'>{values?.errors?.Enderecos?.[0]?.CtbEndCep}</span>
    </div>
  </div>
)}

2. 初始化表单时设置默认touched结构

在Formik的配置中通过initialTouched初始化嵌套结构,确保首次渲染时路径存在:

initialTouched={{
  Enderecos: [
    {
      BairroId: false,
      CtbEndCadMunicipio: false,
      CtbEndTipo: false,
      LograId: false,
      MunId: false,
      CtbEndComplemento: false,
      CtbEndNumero: false,
      CtbEndCep: false,
    }
  ]
}}

3. 使用Formik的getFieldMeta方法获取字段状态

Formik提供的getFieldMeta方法可直接通过字段路径获取touched、error状态,自动处理空值情况:

const cepMeta = values?.getFieldMeta('Enderecos[0].CtbEndCep');

className={clsx(
  'form-control form-control-solid mb-3 mb-lg-0',
  { 'is-invalid': cepMeta.touched && cepMeta.error },
  { 'is-valid': cepMeta.touched && !cepMeta.error }
)}

错误提示部分:

{cepMeta.touched && cepMeta.error && (
  <div className='fv-plugins-message-container'>
    <div className='fv-help-block'>
      <span role='alert'>{cepMeta.error}</span>
    </div>
  </div>
)}

4. 结合空值合并运算符处理默认值

如果担心可选链返回undefined,可配合??设置默认布尔值,确保逻辑判断有效:

{ 
  (values?.touched?.Enderecos?.[0]?.CtbEndCep ?? false) && 
  (values?.errors?.Enderecos?.[0]?.CtbEndCep ?? '') && 
  (
    <div className='fv-plugins-message-container'>
      <div className='fv-help-block'>
        <span role='alert'>{values?.errors?.Enderecos?.[0]?.CtbEndCep}</span>
      </div>
    </div>
  )
}

内容的提问来源于stack exchange,提问作者RAFAEL DA SILVA

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 20:15:42