react-hook-form移除字段后yup仍校验已注销字段的问题求助
解决react-hook-form结合yup动态移除字段后校验异常的问题
核心问题在于yup的静态schema和react-hook-form动态字段的不匹配:你用unregister移除了表单字段,但yup仍会按照初始写死的schema校验所有标记为必填的字段,哪怕这些字段已经不在表单values里。下面是直接可行的解决方案:
方法1:动态生成yup Schema(推荐)
维护一个当前存在的字段列表,每次添加/移除字段时,根据这个列表动态生成对应的yup schema,确保schema只包含当前需要校验的字段。
完整代码示例
import { useForm } from 'react-hook-form'; import { yupResolver } from '@hookform/resolvers/yup'; import * as yup from 'yup'; import { useState, useMemo } from 'react'; function DynamicForm() { // 维护当前存在的字段列表,可根据需求扩展字段配置(比如是否必填) const [activeFields, setActiveFields] = useState([{ name: 'email', required: true }]); // 动态生成yup schema,依赖activeFields更新 const formSchema = useMemo(() => { const schemaShape = {}; activeFields.forEach(field => { schemaShape[field.name] = field.required ? yup.string().required(`${field.name} 不能为空`) : yup.string(); }); return yup.object().shape(schemaShape); }, [activeFields]); const { register, handleSubmit, unregister, formState: { errors } } = useForm({ resolver: yupResolver(formSchema), defaultValues: activeFields.reduce((acc, field) => ({ ...acc, [field.name]: '' }), {}) }); // 添加字段逻辑 const addField = () => { const newFieldName = `field_${Date.now()}`; setActiveFields([...activeFields, { name: newFieldName, required: true }]); }; // 移除字段逻辑 const removeField = (fieldName) => { setActiveFields(activeFields.filter(f => f.name !== fieldName)); unregister(fieldName); }; const onSubmit = (data) => { console.log('提交成功:', data); }; return ( <form onSubmit={handleSubmit(onSubmit)}> {activeFields.map(field => ( <div key={field.name} style={{ margin: '10px 0' }}> <input {...register(field.name)} placeholder={field.name} /> {errors[field.name] && <span style={{ color: 'red' }}>{errors[field.name].message}</span>} <button type="button" onClick={() => removeField(field.name)} style={{ marginLeft: '10px' }}>移除</button> </div> ))} <button type="button" onClick={addField} style={{ marginRight: '10px' }}>添加字段</button> <button type="submit">提交表单</button> </form> ); } export default DynamicForm;
关键说明
- 用
useMemo监听activeFields的变化,每次字段列表更新时重新生成schema,确保schema只包含当前存在的字段。 - 移除字段时同时更新
activeFields和调用unregister,让表单注册状态和schema完全同步。
方法2:使用yup的when条件校验
如果只是个别字段需要动态控制必填规则,可以用yup的when方法,让校验规则依赖于字段是否存在于表单values中:
const formSchema = yup.object().shape({ dynamicField: yup.string().when('', { // 判断当前values中是否包含该字段 is: (values) => Object.prototype.hasOwnProperty.call(values, 'dynamicField'), then: (schema) => schema.required('该字段为必填项'), otherwise: (schema) => schema.notRequired() }) });
这种方法适合字段数量少、规则简单的场景,字段较多时推荐用动态生成schema的方式。
方法3:处理数组型动态字段
如果你的动态字段是数组结构(比如动态添加的表单项),可以直接用yup的array().of()来定义schema,react-hook-form会自动识别数组项的添加/移除,yup只会校验当前数组中存在的项:
const formSchema = yup.object().shape({ items: yup.array().of( yup.object().shape({ name: yup.string().required('名称不能为空'), quantity: yup.number().required('数量不能为空').positive() }) ) });
移除数组项时,调用unregister(items[${index}])即可,yup会自动跳过已移除的项的校验。
内容的提问来源于stack exchange,提问作者Mark Stanley
相关产品推荐
相关产品推荐

