如何为React Hook Form可复用组件的control属性添加严格类型?
为可复用表单组件的
control属性添加严格类型定义 核心解决方案是利用TypeScript泛型结合react-hook-form提供的Control类型,让CreateForm组件能够适配任意表单数据结构,同时保留类型校验能力。
步骤1:改造CreateForm组件的类型定义
首先导入react-hook-form的Control类型,给组件的Props接口添加泛型参数,让control和onSubmit的类型与表单数据结构绑定:
import { Controller, Control, SubmitHandler } from 'react-hook-form'; // 定义带泛型的Props接口,T代表表单数据的结构类型 interface ICreateFormProps<T> { control: Control<T>; onSubmit: SubmitHandler<T>; // 使用react-hook-form提供的SubmitHandler类型,更严谨 } // 组件接收泛型T,自动适配不同的表单结构 const CreateForm = <T,>({ onSubmit, control }: ICreateFormProps<T>) => { return ( <form onSubmit={onSubmit}> {/* 这里的Controller可以根据业务需求扩展,比如动态渲染不同字段 */} <Controller name="name" // 字段名需对应T中的属性,也可以通过props传入实现动态配置 render={({ field, fieldState: { error } }) => ( <StyledTextField {...field} error={!!error} helperText={error?.message} /> )} control={control} rules={{ required: '此字段为必填项' }} // 可选校验规则 /> <button type="submit">提交</button> </form> ); }; export default CreateForm;
步骤2:父组件中使用CreateForm
父组件中useForm已经指定了表单数据类型,传递control和onSubmit时,TypeScript会自动推导CreateForm的泛型参数,无需手动指定:
type CreateFaceInput = { name: string; cover: string; }; const FaceModal = ({ open, onClose }: IFaceModalProps) => { const { handleSubmit, control } = useForm<CreateFaceInput>({ defaultValues: { name: '', cover: '', }, }); const onSubmit: SubmitHandler<CreateFaceInput> = (data) => { // 处理表单提交逻辑 }; return ( <CreateForm onSubmit={handleSubmit(onSubmit)} control={control} /> ); };
如果是其他表单结构的场景,比如{face: string, address: string},只需修改父组件的表单类型定义即可,CreateForm会自动适配:
type CreateAddressInput = { face: string; address: string; }; const AddressModal = ({ open, onClose }) => { const { handleSubmit, control } = useForm<CreateAddressInput>({ defaultValues: { face: '', address: '', }, }); const onSubmit: SubmitHandler<CreateAddressInput> = (data) => { // 处理地址表单提交 }; return ( <CreateForm onSubmit={handleSubmit(onSubmit)} control={control} /> ); };
可选优化:动态渲染表单字段
如果需要CreateForm完全根据不同的表单结构动态生成输入项,可以新增字段配置的Props,让父组件传入需要渲染的字段信息:
// 新增字段配置类型 interface FormFieldConfig<T> { name: keyof T; label: string; type?: 'text' | 'email' | 'password'; rules?: any; // 或者使用react-hook-form的RegisterOptions<T>类型 } // 更新Props接口 interface ICreateFormProps<T> { control: Control<T>; onSubmit: SubmitHandler<T>; fields: FormFieldConfig<T>[]; } // 修改组件渲染逻辑 const CreateForm = <T,>({ onSubmit, control, fields }: ICreateFormProps<T>) => { return ( <form onSubmit={onSubmit}> {fields.map(field => ( <Controller key={field.name as string} name={field.name} render={({ field: ctrlField, fieldState: { error } }) => ( <StyledTextField {...ctrlField} label={field.label} type={field.type || 'text'} error={!!error} helperText={error?.message} /> )} control={control} rules={field.rules} /> ))} <button type="submit">提交</button> </form> ); };
父组件使用时传入字段配置:
<CreateForm onSubmit={handleSubmit(onSubmit)} control={control} fields={[ { name: 'name', label: '名称', rules: { required: '名称不能为空' } }, { name: 'cover', label: '封面地址', rules: { required: '封面地址不能为空' } }, ]} />
内容的提问来源于stack exchange,提问作者qweezz
相关产品推荐
相关产品推荐

