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

React Hook Form+Zod向嵌套表单传递register的TypeScript问题

解决React Hook Form嵌套Zod Schema的register类型兼容问题

核心思路

通过字段路径前缀拼接结合TypeScript类型适配,让复用的子组件能正确接收父组件的register函数,同时匹配嵌套Zod Schema的类型约束。以下是两种可行的实现方案:


方案一:包装register函数适配子组件类型

1. 定义可复用的子Schema与类型

import { z } from "zod";

// 子Schema
export const ValueAppreciationComponentSchema = z.object({
  amount: z.number().min(0, "金额不能为负"),
  rate: z.number().min(0).max(100, "利率范围0-100"),
  period: z.number().min(1, "周期至少为1")
});

// 子表单值类型
export type ValueAppreciationFormValues = z.infer<typeof ValueAppreciationComponentSchema>;

2. 实现复用的子表单组件

子组件接收适配后的register函数,直接使用自身Schema的字段名:

import React from "react";
import { Register } from "react-hook-form";
import { ValueAppreciationFormValues } from "./schema";

type ValueAppreciationComponentProps = {
  register: Register<ValueAppreciationFormValues>;
};

export const ValueAppreciationComponent = ({ register }: ValueAppreciationComponentProps) => {
  return (
    <div className="value-appreciation-group">
      <input
        type="number"
        {...register("amount")}
        placeholder="初始金额"
      />
      <input
        type="number"
        {...register("rate")}
        placeholder="年利率(%)"
      />
      <input
        type="number"
        {...register("period")}
        placeholder="投资周期(年)"
      />
    </div>
  );
};

3. 父组件中包装register并传递

父Schema嵌套子Schema,通过包装函数拼接字段前缀,同时做类型适配:

// 父Schema
import { z } from "zod";
import { ValueAppreciationComponentSchema } from "./schema";

export const InvestmentSchema = z.object({
  investmentName: z.string().min(1, "请输入投资名称"),
  valueAppreciation: ValueAppreciationComponentSchema,
  // 其他父级字段...
});

export type InvestmentFormValues = z.infer<typeof InvestmentSchema>;
import React from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { InvestmentSchema, InvestmentFormValues } from "./schema";
import { ValueAppreciationComponent, ValueAppreciationFormValues } from "./ValueAppreciationComponent";

export const InvestmentForm = () => {
  const { register, handleSubmit } = useForm<InvestmentFormValues>({
    resolver: zodResolver(InvestmentSchema),
    defaultValues: {
      investmentName: "",
      valueAppreciation: {
        amount: 0,
        rate: 5,
        period: 10
      }
    }
  });

  // 包装register,拼接嵌套字段前缀
  const childRegister = <T extends keyof ValueAppreciationFormValues>(name: T) => {
    return register(`valueAppreciation.${name as string}`);
  };

  const onSubmit = (data: InvestmentFormValues) => {
    console.log("提交数据:", data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        type="text"
        {...register("investmentName")}
        placeholder="投资项目名称"
      />
      {/* 传递适配后的register给子组件 */}
      <ValueAppreciationComponent register={childRegister as typeof register<ValueAppreciationFormValues>} />
      <button type="submit">提交表单</button>
    </form>
  );
};

方案二:传递字段前缀,子组件内部拼接路径

这种方式无需包装register,子组件通过接收的前缀拼接完整字段路径,类型更安全:

1. 修改子组件Props

import { Register, FieldPath } from "react-hook-form";

type ValueAppreciationComponentProps<T extends string> = {
  register: Register<any>;
  fieldPrefix: T;
};

2. 子组件内部拼接路径

export const ValueAppreciationComponent = <T extends string>({ register, fieldPrefix }: ValueAppreciationComponentProps<T>) => {
  return (
    <div className="value-appreciation-group">
      <input
        type="number"
        {...register(`${fieldPrefix}.amount` as const)}
        placeholder="初始金额"
      />
      <input
        type="number"
        {...register(`${fieldPrefix}.rate` as const)}
        placeholder="年利率(%)"
      />
      <input
        type="number"
        {...register(`${fieldPrefix}.period` as const)}
        placeholder="投资周期(年)"
      />
    </div>
  );
};

3. 父组件调用子组件

直接传递register和嵌套字段前缀即可:

<ValueAppreciationComponent 
  register={register} 
  fieldPrefix="valueAppreciation" 
/>

方案说明

  • 方案一优势:子组件逻辑更简洁,完全不用关心嵌套层级,适合固定结构的子表单复用
  • 方案二优势:无需类型断言,TypeScript可自动推断字段路径的合法性,父组件控制更灵活

内容的提问来源于stack exchange,提问作者Federico Taschin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 15:42:10