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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 07:57:05