React Hook Form跨组件校验:子组件验证父级表单数据
分节验证动态表单的实现方案
基于react-hook-form和useFieldArray,你可以通过针对性触发指定字段验证的方式实现分节校验需求,核心思路是把每个Section对应表单的嵌套字段组,用trigger方法单独验证前一节的所有字段。
核心实现步骤
1. 定义嵌套式表单结构
将每个Section的数据封装为表单的嵌套对象,动态字段用数组存储,示例结构如下:
const defaultValues = { section1: { fields: [{ name: '', value: '' }] // Section1的动态字段初始值 }, section2: { fields: [{ item: '', quantity: '' }] // Section2的动态字段初始值 }, // 更多Section可按此格式扩展 };
2. 初始化useForm并配置验证规则
在根组件中初始化useForm,为每个Section的字段设置验证规则,同时用useFieldArray管理各Section的动态字段:
import { useForm, useFieldArray, useState } from 'react-hook-form'; function App() { const { register, control, trigger, getValues, formState: { errors } } = useForm({ defaultValues, mode: 'onChange' // 可选,开启实时字段验证 }); // 初始化Section1的动态字段管理 const { fields: section1Fields, append: appendSection1, remove: removeSection1 } = useFieldArray({ control, name: 'section1.fields' }); // 初始化Section2的动态字段管理 const { fields: section2Fields, append: appendSection2, remove: removeSection2 } = useFieldArray({ control, name: 'section2.fields' }); // 控制Section的显示状态 const [showSection2, setShowSection2] = useState(false); const [showSection3, setShowSection3] = useState(false);
3. 封装Section组件(复用增删与验证逻辑)
把每个Section的渲染、动态字段增删、前节验证逻辑封装成可复用组件:
function Section({ prevSectionName, currentSectionName, fields, append, remove, register, trigger, getValues, errors, onValidatePass }) { const handleValidatePrev = async () => { // 触发前一节所有字段的验证 const isValid = await trigger(prevSectionName); if (isValid) { // 验证通过后,可获取前一节数据做提交或其他处理 const prevData = getValues(prevSectionName); console.log(`${prevSectionName} 数据:`, prevData); // 执行后续逻辑,比如显示下一节 onValidatePass?.(); } }; return ( <div className="section"> <h3>{currentSectionName}</h3> {/* 渲染动态字段组 */} {fields.map((item, index) => ( <div key={item.id} className="field-row"> {/* Section1专属字段 */} {currentSectionName === 'Section 1' && ( <> <input {...register(`section1.fields.${index}.name`, { required: '名称不能为空' })} placeholder="名称" /> {errors?.section1?.fields?.[index]?.name && ( <span className="error-text">{errors.section1.fields[index].name.message}</span> )} <input {...register(`section1.fields.${index}.value`, { required: '值不能为空', pattern: { value: /^\d+$/, message: '必须是数字' } })} placeholder="数值" /> {errors?.section1?.fields?.[index]?.value && ( <span className="error-text">{errors.section1.fields[index].value.message}</span> )} </> )} {/* Section2专属字段 */} {currentSectionName === 'Section 2' && ( <> <input {...register(`section2.fields.${index}.item`, { required: '项目不能为空' })} placeholder="项目名称" /> {errors?.section2?.fields?.[index]?.item && ( <span className="error-text">{errors.section2.fields[index].item.message}</span> )} <input {...register(`section2.fields.${index}.quantity`, { required: '数量不能为空', min: { value: 1, message: '数量不能小于1' } })} type="number" placeholder="数量" /> {errors?.section2?.fields?.[index]?.quantity && ( <span className="error-text">{errors.section2.fields[index].quantity.message}</span> )} </> )} <button type="button" onClick={() => remove(index)}>删除</button> </div> ))} <button type="button" onClick={() => append({})}>添加字段</button> {/* 仅非首节显示验证前节的按钮 */} {prevSectionName && ( <button type="button" className="validate-btn" onClick={handleValidatePrev}> 验证{prevSectionName}并继续 </button> )} </div> ); }
4. 在根组件中渲染各Section并控制显示
通过状态控制Section的显示,只有前一节验证通过后才展示下一节:
return ( <form className="form-container"> <Section prevSectionName={null} currentSectionName="Section 1" fields={section1Fields} append={appendSection1} remove={removeSection1} register={register} trigger={trigger} getValues={getValues} errors={errors} onValidatePass={() => setShowSection2(true)} /> {showSection2 && ( <Section prevSectionName="section1" currentSectionName="Section 2" fields={section2Fields} append={appendSection2} remove={removeSection2} register={register} trigger={trigger} getValues={getValues} errors={errors} onValidatePass={() => setShowSection3(true)} /> )} {/* Section3及后续可按相同方式扩展 */} </form> ); }
关键细节说明
- 精准验证:
trigger('section1')会自动验证section1下的所有字段,包括useFieldArray生成的动态字段,无需手动遍历。 - 错误处理:通过
errors.sectionX.fields[index].fieldName的方式访问动态字段的错误信息,使用可选链避免空值报错。 - 分步数据处理:验证通过后,可调用
getValues('sectionX')获取该节的完整数据,单独提交或缓存。
内容的提问来源于stack exchange,提问作者Janani
相关产品推荐
相关产品推荐

