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

Next.js 14.2.5 + Zod 3.23.8 文件上传表单Schema配置失败求助

问题解决:文件上传表单Zod验证失败+受控输入警告

问题根源

  1. Zod验证失败:文件输入框的默认field.value是FileList类型(包含选中的所有文件),而非单个File对象,所以你之前的两种Schema都无法匹配正确的输入类型。另外方案2的refine语法存在错误(箭头函数返回值写法不符合规范)。
  2. 受控输入警告:表单初始值未明确设置,且默认的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 02:37:09