Next.js 14.2.5 + Zod 3.23.8 文件上传表单Schema配置失败求助
问题解决:文件上传表单Zod验证失败+受控输入警告
问题根源
- Zod验证失败:文件输入框的默认
field.value是FileList类型(包含选中的所有文件),而非单个File对象,所以你之前的两种Schema都无法匹配正确的输入类型。另外方案2的refine语法存在错误(箭头函数返回值写法不符合规范)。 - 受控输入警告:表单初始值未明确设置,且默认的
onChange逻辑不匹配文件输入框的特性,导致React无法识别输入状态的一致性。
修复步骤
1. 修正Zod Schema
针对单个文件验证场景,调整Schema为验证File对象,并补充正确的验证规则:
import { z } from "zod"; // 定义.xlsx文件类型检查函数 const isXlsxFile = (file: File) => { return file.type === "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; }; export const fileUploadSchema = z.object({ file: z.instanceof(File) .refine(file => file.size > 0, "文件不能为空") .refine(isXlsxFile, "仅支持.xlsx格式的文件") });
2. 修正表单组件的文件输入处理
文件输入框的onChange和value需要特殊处理,不能直接沿用默认的field属性:
<Form {...form}> <form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col gap-4"> <FormField control={form.control} name="file" render={({ field }) => ( <FormItem> <FormLabel className="block mb-2 text-sm font-medium text-gray-900 dark:text-white">File</FormLabel> <FormControl> <Input className="block w-full text-sm text-gray-900 border border-gray-300 rounded-lg cursor-pointer bg-gray-50 dark:text-gray-400 focus:outline-none dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400" type="file" // 自定义onChange,将选中的第一个文件传递给表单 onChange={(e) => field.onChange(e.target.files?.[0])} // 处理value避免受控警告:file input的value只能是字符串或null,这里用空字符串匹配初始状态 value={field.value ? "" : undefined} // 直接限制可选文件类型,提升用户体验 accept=".xlsx" /> </FormControl> <FormMessage /> </FormItem> )} /> <DialogFooter className="gap-2 pt-2 sm:space-x-0"> <DialogClose asChild> <Button type="button" variant="outline"> Cancel </Button> </DialogClose> <Button disabled={isUpdatePending}> {isUpdatePending && ( <ReloadIcon className="mr-2 size-4 animate-spin" aria-hidden="true" /> )} Submit </Button> </DialogFooter> </form> </Form>
3. 明确表单初始值
创建表单时设置明确的默认值,避免React认为输入状态切换:
import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { fileUploadSchema } from "./your-schema-path"; const form = useForm<z.infer<typeof fileUploadSchema>>({ defaultValues: { file: undefined }, resolver: zodResolver(fileUploadSchema) });
额外说明
- 方案2的语法错误:原代码中
.refine((file: File) => {file?.size !== 0, "File is required"})是错误写法,正确规则应为箭头函数返回布尔值,错误信息作为第二个参数的message属性,比如:.refine(file => file?.size !== 0, { message: "File is required" })。 - 使用
accept=".xlsx"可以让浏览器直接过滤可选文件,减少用户选择错误文件的概率。
内容的提问来源于stack exchange,提问作者H.Sheng
相关产品推荐
相关产品推荐

