如何通过Zod自动检测表单字段是否可选,为必填项添加*标记?
从Zod中提取字段可选性,自动为必填项添加*标记
是的,你可以直接从Zod Schema中提取字段的可选性信息,从而自动为必填项的标签添加*标记。以下是具体实现步骤:
1. 编写字段必填性检测函数
首先,我们需要一个辅助函数,根据字段路径(支持嵌套字段)遍历Zod Schema,判断该字段是否为必填项:
import { z } from 'zod'; const isFieldRequired = (schema: z.ZodObject<any>, fieldPath: string): boolean => { const segments = fieldPath.split('.'); let currentSchema = schema; for (const segment of segments) { if (!currentSchema.shape[segment]) { throw new Error(`无效的字段路径: ${fieldPath}`); } currentSchema = currentSchema.shape[segment]; } // 若字段不是可选的,则为必填项 return !currentSchema.isOptional; };
2. 修改自定义表单字段组件
在你的CustomFormInput组件中,导入Zod Schema并使用上述函数判断字段是否必填,然后在标签中添加*:
import { FormSchema } from './path-to-your-schema'; // 替换为你的Schema路径 // 其他必要导入(如FormField、FormItem等) interface CustomFormInputProps { isLoading: boolean; control: Control<z.infer<typeof FormSchema>>; name: FieldPath<z.infer<typeof FormSchema>>; label: string; placeholder: string; } const CustomFormInput = ({ isLoading, control, name, label, placeholder }: CustomFormInputProps) => { const isRequired = isFieldRequired(FormSchema, name); return ( <FormField disabled={isLoading} control={control} name={name} render={(field) => ( <FormItem> <FormLabel> {label} {isRequired && <span className="text-red-500 ml-1">*</span>} </FormLabel> <FormControl> <Input type={name === 'password' ? 'password' : 'text'} placeholder={placeholder} {...field} /> </FormControl> <FormMessage /> </FormItem> )} /> ); }; export default CustomFormInput;
3. 让组件支持多Schema复用(可选)
如果你希望组件能适配不同的Zod Schema,可以将Schema作为props传入,增强组件的复用性:
import { z } from 'zod'; import { Control, FieldPath } from 'react-hook-form'; // 其他必要导入 interface CustomFormInputProps<T extends z.ZodObject<any>> { isLoading: boolean; control: Control<z.infer<T>>; name: FieldPath<z.infer<T>>; label: string; placeholder: string; schema: T; } const isFieldRequired = <T extends z.ZodObject<any>>(schema: T, fieldPath: string): boolean => { const segments = fieldPath.split('.'); let currentSchema: any = schema; for (const segment of segments) { if (!currentSchema.shape[segment]) { throw new Error(`无效的字段路径: ${fieldPath}`); } currentSchema = currentSchema.shape[segment]; } return !currentSchema.isOptional; }; const CustomFormInput = <T extends z.ZodObject<any>>({ isLoading, control, name, label, placeholder, schema }: CustomFormInputProps<T>) => { const isRequired = isFieldRequired(schema, name); return ( <FormField disabled={isLoading} control={control} name={name} render={(field) => ( <FormItem> <FormLabel> {label} {isRequired && <span className="text-red-500 ml-1">*</span>} </FormLabel> <FormControl> <Input type={name === 'password' ? 'password' : 'text'} placeholder={placeholder} {...field} /> </FormControl> <FormMessage /> </FormItem> )} /> ); }; export default CustomFormInput;
使用时传入对应的Schema:
<CustomFormInput isLoading={false} control={control} name="email" label="Email" placeholder="Enter your email" schema={FormSchema} />
注意事项
- 若字段是
nullable(如z.string().nullable()),isOptional会返回false,如果需要将nullable字段也视为非必填,可以修改判断逻辑为return !currentSchema.isOptional && !currentSchema.isNullable; - 对于包含
undefined的联合类型(如z.string().or(z.undefined())),isOptional会返回true,等同于.optional()的效果
内容的提问来源于stack exchange,提问作者Rendo Hugh Mann Sama
相关产品推荐
相关产品推荐

