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

上传多张图片时Zod验证失败:预期数组却收到对象的问题求助

解决Zod验证错误及实现图片上传流程

问题根源

你手动维护了tourImages状态却未同步到React Hook Form(RHF)的表单状态中,导致RHF拿到的values.images是input原生的File对象而非数组,与Zod Schema要求的数组类型冲突,触发Expected array received object错误。同时,表单阶段的images是File数组,但Zod Schema验证的是最终提交的string类型URL数组,类型不匹配也会引发验证失败。

解决方案步骤

1. 统一用RHF管理表单状态,移除自定义useState

删除独立维护的tourImages状态,改用RHF的setValue同步文件数组到表单字段:

import { useForm, SubmitHandler } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";

// 表单输入阶段Schema:验证上传的File数组
const createTourFormSchema = z.object({
  // 其他表单字段(如title、description等)按需添加
  images: z.array(z.instanceof(File)),
});

// 提交后端阶段Schema:验证最终的URL数组
const createTourSubmitSchema = z.object({
  // 其他字段与表单Schema保持一致
  images: z.array(z.string()),
});

type ICreateTourFormInput = z.infer<typeof createTourFormSchema>;

export const TourCreateForm = () => {
  const { register, handleSubmit, setValue } = useForm<ICreateTourFormInput>({
    resolver: zodResolver(createTourFormSchema),
    defaultValues: {
      images: [],
      // 其他字段默认值
    },
  });

  // 处理图片选择事件
  const handleImagesChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    const files = event.target.files;
    if (files) {
      const fileList = Object.values(files) as File[];
      // 同步文件数组到RHF的images字段
      setValue("images", fileList);
    }
  };

2. 修正表单输入组件的绑定

将RHF的register props传递给自定义FormInput,确保RHF能跟踪字段变化:

<FormInput
  name="images"
  labelText="Tour Images"
  type="file"
  multiple={true}
  onChange={handleImagesChange}
  {...register("images")} // 关键:绑定RHF注册器
/>

3. 调整提交逻辑,先上传图片再提交数据

从RHF的values中获取File数组,上传到Cloudinary拿到URL数组后,构造符合后端要求的数据提交:

const onSubmitHandler: SubmitHandler<ICreateTourFormInput> = async (values) => {
  const uploadedImageUrls: string[] = [];

  // 批量上传图片到Cloudinary
  for (const image of values.images) {
    const imageFormData = new FormData();
    imageFormData.append("file", image);
    imageFormData.append(
      "upload_preset",
      import.meta.env.VITE_CLOUDINARY_UPLOAD_PRESET
    );

    try {
      const imageUploadRes = await uploadImage(imageFormData);
      const imageUrl = (imageUploadRes as any)?.data?.secure_url;
      if (imageUrl) uploadedImageUrls.push(imageUrl);
    } catch (err) {
      console.error("图片上传失败:", err);
      // 按需添加错误提示逻辑
      return;
    }
  }

  // 构造最终提交给后端的数据
  const submitData = {
    ...values,
    imageCover: imageCoverUrl, // 确保imageCoverUrl是已上传的封面图URL
    images: uploadedImageUrls,
  };

  // 可选:用提交Schema验证最终数据
  const validation = createTourSubmitSchema.safeParse(submitData);
  if (!validation.success) {
    console.error("提交数据验证失败:", validation.error);
    return;
  }

  // 调用RTK mutation提交数据
  await createTour(submitData);
};

4. 绑定提交函数到表单

确保表单的onSubmit绑定到RHF的handleSubmit:

<form onSubmit={handleSubmit(onSubmitHandler)}>
  {/* 其他表单字段 */}
  <FormInput {.../* 图片输入组件的props */} />
  <button type="submit">创建旅行</button>
</form>

关键说明

  • 拆分Zod Schema为表单输入Schema和提交Schema,分别适配File数组和URL数组的验证需求
  • 所有表单字段状态统一由RHF管理,避免自定义状态与RHF状态不一致的问题
  • 图片上传过程中添加错误捕获,可按需调整为单张失败后继续上传其他图片

内容的提问来源于stack exchange,提问作者Ayush

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 18:50:36