使用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
相关产品推荐
相关产品推荐

