React Hook Form结合Yup验证嵌套属性失效问题排查
问题:嵌套表单字段验证失效
当前表单可提交至后端,但social.facebook和social.instagram字段的Yup验证完全失效,仅name字段验证正常,怀疑问题出在封装的Form组件泛型定义上。
校验规则与类型定义
const createEmployeeSchema = yup.object().shape({ name: yup.string().max(100).required(), social: yup.object().shape({ facebook: yup.string().required(), instagram: yup.string().required(), }) }) type CreateEmployeeDTO = { name: string social: { facebook: string instagram: string } }
CreateEmployee表单组件
export const CreateEmployee = () => { const createEmployeeQuery = useCreateEmployeeQuery() return ( <Form<CreateEmployeeDTO, typeof createEmployeeSchema> id="create-employee" onSubmit={async (values) => { createEmployeeQuery.mutateAsync(values) }} schema={createEmployeeSchema} > {(methods) => ( <> <Typography fontWeight={600}>Employee Information</Typography> <TextFieldMUI standard small name={'name'} label={'Name'} /> <TextFieldMUI standard small name={'social.facebook'} label={'Facebook'} /> <TextFieldMUI standard small name={'social.instagram'} label={'Instagram'} /> <Button>Submit</Button> </> )} </Form> ) }
封装的Form组件(原代码)
import { yupResolver } from '@hookform/resolvers/yup'; import clsx from 'clsx'; import * as React from 'react'; import { FormProvider, SubmitHandler, UseFormProps, UseFormReturn, useForm } from 'react-hook-form'; import * as yup from 'yup'; type FormProps<TFormValues, Schema> = { className?: string; onSubmit: SubmitHandler<TFormValues>; children: (methods: UseFormReturn<TFormValues>) => React.ReactNode; options?: UseFormProps<TFormValues>; id?: string; schema?: Schema; }; export const Form = < TFormValues extends Record<string, unknown> = Record<string, unknown>, Schema extends yup.ObjectSchema<yup.AnyObject> = yup.ObjectSchema<yup.AnyObject> >({ onSubmit, children, className, options, id, schema, }: FormProps<TFormValues, Schema>) => { const methods = useForm<TFormValues>({ ...options, resolver: schema && yupResolver(schema) }); return ( <FormProvider {...methods} > <form className={clsx('space-y-6', className)} onSubmit={methods.handleSubmit(onSubmit)} id={id} > {children(methods)} </form> </FormProvider> ); };
问题分析
问题确实出在Form组件的泛型约束上。原代码中Schema extends yup.ObjectSchema<yup.AnyObject>的定义会丢失嵌套对象的类型细节,导致yupResolver无法正确解析嵌套字段的校验规则,最终使得social.facebook和social.instagram的验证不生效。
解决方案
修改Form组件的泛型定义,让Schema与TFormValues直接关联,确保类型信息完整传递:
import { yupResolver } from '@hookform/resolvers/yup'; import clsx from 'clsx'; import * as React from 'react'; import { FormProvider, SubmitHandler, UseFormProps, UseFormReturn, useForm } from 'react-hook-form'; import * as yup from 'yup'; // 简化泛型,让Schema直接关联TFormValues的类型 type FormProps<TFormValues> = { className?: string; onSubmit: SubmitHandler<TFormValues>; children: (methods: UseFormReturn<TFormValues>) => React.ReactNode; options?: UseFormProps<TFormValues>; id?: string; schema?: yup.ObjectSchema<TFormValues>; }; export const Form = <TFormValues extends Record<string, unknown> = Record<string, unknown>>({ onSubmit, children, className, options, id, schema, }: FormProps<TFormValues>) => { const methods = useForm<TFormValues>({ ...options, resolver: schema ? yupResolver(schema) : undefined }); return ( <FormProvider {...methods} > <form className={clsx('space-y-6', className)} onSubmit={methods.handleSubmit(onSubmit)} id={id} > {children(methods)} </form> </FormProvider> ); };
同时,调用Form组件时无需再手动指定Schema泛型,简化代码:
<Form<CreateEmployeeDTO> id="create-employee" onSubmit={async (values) => { createEmployeeQuery.mutateAsync(values) }} schema={createEmployeeSchema} > {/* 子组件内容保持不变 */} </Form>
另外确认TextFieldMUI组件正确传递name属性(如social.facebook),react-hook-form原生支持这种点分隔的嵌套字段命名方式,只要组件正确映射name到底层input元素即可。
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

