如何从Zod枚举动态生成shadcn表单的SelectItem?
从Zod枚举动态生成shadcn Select选项的可行方案
当然可行,这是避免重复维护枚举选项的最佳实践之一,核心思路是从Zod定义的枚举类型中提取枚举值,转换为shadcn Select组件所需的SelectItem结构(通常是包含value和label的对象)。
具体实现步骤与代码示例
1. 定义包含枚举字段的Zod Schema
首先确保你的Zod Schema中明确使用枚举类型(可以是Zod原生的z.enum或绑定TS原生枚举的z.nativeEnum):
import { z } from "zod"; // 方式1:使用Zod原生枚举 const Field3ZodEnum = z.enum(["USER_ADMIN", "USER_EDITOR", "USER_VIEWER", "USER_GUEST"]); // 方式2:使用TS原生枚举(可选) enum Field3NativeEnum { Admin = "USER_ADMIN", Editor = "USER_EDITOR", Viewer = "USER_VIEWER", Guest = "USER_GUEST", } // 最终的表单验证Schema const FormSchema = z.object({ field1: z.string(), field2: z.string(), // 二选一:使用Zod枚举或TS原生枚举 field3: Field3ZodEnum, // field3: z.nativeEnum(Field3NativeEnum), });
2. 编写工具函数生成SelectItem数组
编写通用函数从Zod枚举中提取选项,转换为符合shadcn要求的格式:
// 针对Zod原生枚举的工具函数 const getSelectItemsFromZodEnum = <T extends z.ZodEnum<any>>(zodEnum: T) => { return zodEnum.options.map(option => ({ value: option, // 可选:格式化显示文本,比如把下划线替换为空格并转大写 label: option.replace(/_/g, " ").toUpperCase() })); }; // 针对TS原生枚举的工具函数(如果用了nativeEnum) const getSelectItemsFromNativeEnum = <T extends object>(nativeEnum: T) => { return Object.entries(nativeEnum).map(([labelKey, value]) => ({ value, // 可选:把枚举键名(驼峰)格式化为友好文本 label: labelKey.replace(/([A-Z])/g, " $1").trim() })); }; // 生成field3的Select选项数组 const field3Options = getSelectItemsFromZodEnum(Field3ZodEnum); // 如果用TS原生枚举则用这个: // const field3Options = getSelectItemsFromNativeEnum(Field3NativeEnum);
3. 在shadcn Select组件中使用动态选项
直接将生成的选项数组渲染到Select组件中:
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; // 假设已经用react-hook-form结合Zod处理表单逻辑 import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; export function MyForm() { const form = useForm({ resolver: zodResolver(FormSchema), defaultValues: { field1: "", field2: "", field3: "" } }); return ( <form onSubmit={form.handleSubmit((data) => console.log(data))}> {/* 其他表单字段... */} <Select {...form.register("field3")}> <SelectTrigger> <SelectValue placeholder="选择用户角色" /> </SelectTrigger> <SelectContent> {field3Options.map(option => ( <SelectItem key={option.value} value={option.value}> {option.label} </SelectItem> ))} </SelectContent> </Select> <button type="submit">提交</button> </form> ); }
优势说明
- 只需要维护一处枚举定义(Zod或TS枚举),修改枚举值后Select选项会自动同步,彻底避免重复维护的问题
- 工具函数可以复用在其他需要从枚举生成Select选项的场景中
- 格式化逻辑可以根据需求灵活调整,比如自定义显示文本的样式
内容的提问来源于stack exchange,提问作者Cheekumz
相关产品推荐
相关产品推荐

