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

如何为React函数式组件传入泛型,使Props扩展接口并支持动态表单

React Hook Form 动态输入组件泛型改造问题

场景说明

我正在使用react-hook-form的FormProvider创建动态输入表单,顶层代码如下:

const formMethods = useForm<TSongFormData>();
return (
    <FormProvider {...formMethods}>
       <SongInputForm />
    </FormProvider>
)

在SongInputForm组件中实现了实际表单结构,包含自定义的InputElement输入组件:

const SongForm = () => {
  const { handleSubmit } = useFormContext<TSongFormData>();
  return (
        <form
          onSubmit={handleSubmit((data) => console.log("data", data))}
        >
          <InputElement label="Song Name" name="name" />
          <InputElement label="Author" name="author" />
          <input type="submit" />
        </form>
    );
  };
}

InputElement组件接收原生input的所有props,其中name属性被约束为表单数据键的联合类型TSongFormDataKey,当前硬编码类型的实现如下:

interface ITextInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
  label?: string;
  name: TSongFormDataKey;
}

const InputElement = ({ name, label, ...restProps }: ITextInputProps) => {
  const { register } = useFormContext<TSongFormData>();
  return (
    <div>
      <label htmlFor={name}>{label}</label>
      <input id={name} {...restProps} {...register(name)} />
    </div>
  );
};

需求与问题

当前InputElement组件硬编码了TSongFormData和TSongFormDataKey类型,无法复用在其他结构的表单中。我希望将这两个类型通过泛型传入组件,实现组件动态化,提升Props类型的灵活性。期望的实现形式大致如下:

interface ITextInputProps<T> extends React.InputHTMLAttributes<HTMLInputElement> {
  label?: string;
  name: T;
}

const InputElement = <T,K>({ name, label, ...restProps }: ITextInputProps<T>) => {
  const { register } = useFormContext<K>();
  return (
    <div>
      <label htmlFor={name}>{label}</label>
      <input id={name} {...restProps} {...register(name)} />
    </div>
  );
};

其中T对应TSongFormDataKey(表单键联合类型),K对应TSongFormData(表单数据类型)。

解决方案

我们可以只声明一个表单数据类型的泛型,通过keyof操作符自动推导键的类型,既保证类型关联,又简化泛型参数:

import React from 'react';
import { useFormContext, RegisterOptions } from 'react-hook-form';

// 定义组件Props,泛型TFormData为表单数据的类型
interface ITextInputProps<TFormData> extends React.InputHTMLAttributes<HTMLInputElement> {
  label?: string;
  name: keyof TFormData;
  // 可选:支持传入react-hook-form的校验规则
  registerOptions?: RegisterOptions<TFormData>;
}

// 组件泛型声明,约束TFormData为对象类型
const InputElement = <TFormData extends Record<string, unknown>>({
  name,
  label,
  registerOptions,
  ...restProps
}: ITextInputProps<TFormData>) => {
  const { register } = useFormContext<TFormData>();
  
  return (
    <div>
      {label && <label htmlFor={String(name)}>{label}</label>}
      <input
        id={String(name)}
        {...restProps}
        {...register(name, registerOptions)}
      />
    </div>
  );
};

使用方式

在表单组件中使用时,TypeScript会自动通过useFormContext推断出泛型类型,无需手动传入:

const SongForm = () => {
  const { handleSubmit } = useFormContext<TSongFormData>();
  return (
    <form onSubmit={handleSubmit((data) => console.log("data", data))}>
      <InputElement label="Song Name" name="name" />
      <InputElement label="Author" name="author" />
      <input type="submit" />
    </form>
  );
};

关键说明

  1. TFormData extends Record<string, unknown>确保泛型参数是对象类型,符合表单数据的结构要求
  2. name: keyof TFormData保证name只能是表单数据类型的合法键,避免类型错误
  3. 新增registerOptions参数,支持传入react-hook-form的校验规则,增强组件实用性
  4. 使用String(name)将键转换为字符串,兼容非字符串类型的键(表单键通常为字符串,此处理提升兼容性)

改造后的InputElement可以复用在任何由FormProvider包裹的表单中,完全适配不同结构的表单数据类型。

内容的提问来源于stack exchange,提问作者Jerome Marshall

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 19:30:49