如何在next-cloudinary与react-hook-form中实现多图上传存储
多图上传解决方案(基于shadcn/ui、react-hook-form、Next-Cloudinary)
一、修改ImageUpload组件的Props和上传逻辑
调整组件支持数组形式的URL存储,同时增加多图展示、移除功能:
"use client"; import React, { useCallback } from "react"; import Image from "next/image"; import { CldUploadWidget } from "next-cloudinary"; import { ImagePlus, X } from "lucide-react"; declare global { var cloudinary: any; } // 调整Props:onChange接受字符串数组 interface ImageUploadProps { value: string[]; onChange: (value: string[]) => void; disabled?: boolean; } const ImageUpload: React.FC<ImageUploadProps> = ({ value, onChange, disabled }) => { const handleUpload = useCallback( (result: any) => { // 修复Cloudinary字段拼写错误:scure_url -> secure_url const newUrl = result.info.secure_url; // 将新URL追加到现有数组,避免覆盖原有数据 onChange([...value, newUrl]); }, [onChange, value] ); // 移除单张图片的逻辑 const handleRemoveImage = (index: number) => { onChange(value.filter((_, i) => i !== index)); }; return ( <div className="space-y-4"> <CldUploadWidget onUpload={handleUpload} options={{ maxFiles: 5 }} uploadPreset="uxvsxycu" disabled={disabled} > {({ open }) => ( <div onClick={() => !disabled && open?.()} className={`p-4 border-4 cursor-pointer border-dashed border-primary rounded-lg hover:opacity-75 transition flex flex-col space-y-2 items-center justify-center ${disabled ? "opacity-50 cursor-not-allowed" : ""}`} > <div className="relative flex items-center justify-center lg:h-40 lg:w-96 mx-auto"> <div className="flex flex-col space-y-5 justify-center items-center w-full h-full"> <ImagePlus size={70} color="gray" /> <div className="font-semibold text-lg"> 点击上传图片(最多5张) </div> </div> </div> </div> )} </CldUploadWidget> {/* 展示已上传的所有图片 */} {value.length > 0 && ( <div className="grid grid-cols-2 md:grid-cols-3 gap-4"> {value.map((url, index) => ( <div key={index} className="relative group"> <Image alt={`已上传图片 ${index + 1}`} src={url} width={200} height={200} className="rounded-lg object-cover w-full h-40" /> <button onClick={() => handleRemoveImage(index)} className="absolute top-2 right-2 bg-red-500 text-white p-1 rounded-full opacity-0 group-hover:opacity-100 transition-opacity" > <X size={16} /> </button> </div> ))} </div> )} </div> ); }; export default ImageUpload;
二、父组件FormField配置调整
直接传递react-hook-form的field.onChange即可,它原生支持数组类型的更新:
// 父组件中images字段的FormField部分 <FormField control={form.control} name="images" render={({ field }) => ( <FormItem className="flex flex-col space-y-5"> <FormLabel className="font-bold text-base lg:text-3xl"> 上传图片 </FormLabel> <FormControl> <ImageUpload disabled={isLoading} onChange={field.onChange} value={field.value} /> </FormControl> <FormDescription> 上传项目相关图片,最多5张。 </FormDescription> <FormMessage /> </FormItem> )} />
三、关键修改说明
- 数组化存储:将
onChange参数改为数组类型,每次上传追加新URL而非覆盖。 - 修复字段错误:修正Cloudinary返回的URL字段拼写(
scure_url→secure_url)。 - 交互优化:增加多图网格展示和单图移除功能,提升用户体验。
- 禁用状态处理:表单提交中(
isLoading)禁止上传操作,避免重复提交。
四、Zod校验保持原配置(可按需增强)
原有schema已支持数组类型,可根据需求添加数量约束:
const formSchema = z.object({ title: z.string().min(5, { message: "Title must be at least 5 characters.", }), caption: z.string().min(10, { message: "Caption must be at least 10 characters.", }), // 可选:添加最少上传1张图片的约束 images: z.array(z.string()).min(1, "请至少上传一张图片"), });
内容的提问来源于stack exchange,提问作者Ismail Salah
相关产品推荐
相关产品推荐

