Vite+TS注册页中Yup的Avatar必填验证未显示问题求助
问题:头像未选择时未触发必填验证,直接显示文件类型错误
使用Vite+TypeScript开发注册页面,采用react-hook-form处理表单、Yup做验证。表单包含username、fullname、email、password和avatar(头像文件)5个字段。为所有字段设置了必填规则,但未选择头像时,未触发“Avatar is required”提示,反而直接显示文件类型无效的错误。
相关代码
验证Schema代码
const validFileExtensions = ["jpg", "jpeg", "png", "gif", "svg", "webp"]; function isValidFileType(fileName?: string): boolean { if (!fileName) return false; const extension = fileName.split(".").pop()?.toLowerCase(); return extension ? validFileExtensions.includes(extension) : false; } // validation schema with yup const validationSchema = Yup.object().shape({ email: Yup.string().required("Email is required").email("Email is invalid"), password: Yup.string() .required("Password is required") .min(8, "Password must be at least 8 characters long"), fullname: Yup.string().required("Full name is required"), username: Yup.string() .required("Username is required") .min(4, "At least 4 characters ") .matches(/^\S+$/, "No spaces allowed"), avatar: Yup.mixed<File>() .required("Avatar is required") .test( "fileType", "Only JPG, JPEG, PNG, GIF, SVG, and WEBP files are allowed", (value) => { if (!value) return true; return isValidFileType(value.name); } ) .test("fileSize", "File must be less than 100KB", (value) => { if (!value) return true; return value.size <= MAX_FILE_SIZE; }), // --- 注释的旧测试逻辑 --- // .test("fileType", "Unsupported file format", (value) => { // if (!value || value.length === 0) return false; // const file = value[0]; // return ["image/jpeg", "image/png"].includes(file.type); // }) // .test("fileSize", "File must be less than 2MB", (value) => { // if (!value || value.length === 0) return false; // const file = value[0]; // return file.size <= 2 * 1024 * 1024; // }), --- });
react-hook-form初始化代码
const { register, handleSubmit, formState: { errors }, } = useForm<SignUpFormInputs>({ resolver: yupResolver(validationSchema), });
传递错误给UploadFileInput组件代码
<UploadFileInput label="Upload an Avatar" id="uploadAvatar" name="avatar" errors={errors.avatar?.message} register={register} />
UploadFileInput组件代码
import React from "react"; import { UseFormRegister } from "react-hook-form"; import { RxAvatar } from "react-icons/rx"; interface SignUpFormInputs { email: string; password: string; fullname: string; username: string; avatar: File; } interface UploadFileInputProps { id: string; label: string; name: keyof SignUpFormInputs; errors?: string; register: UseFormRegister<SignUpFormInputs>; } const UploadFileInput: React.FC<UploadFileInputProps> = ({ id, label, name, register, errors, }) => { return ( <> <label className="text-[17px] w-[50%] text-center text-gray-400 text-sm bg-gray-100 file:cursor-pointer p-2 cursor-pointer file:text-white" htmlFor={id} > <span className=" flex items-center gap-2"> <RxAvatar size={30} /> {label} </span> <input id={id} type="file" className="hidden" {...register(name)} /> {errors && ( <p className="absolute ml-[-9px] text-red-500 text-sm mt-3 "> {errors} </p> )} </label> </> ); }; export default UploadFileInput;
问题截图

解决方案
核心问题是react-hook-form默认会将文件输入的值解析为字符串(而非File对象),导致Yup的required规则和自定义测试逻辑出现异常。按以下步骤修复:
1. 注册文件输入时启用valueAsFile
修改UploadFileInput组件中的文件输入注册逻辑,让react-hook-form将值解析为File对象:
<input id={id} type="file" className="hidden" {...register(name, { valueAsFile: true })} />
2. 修正类型定义
将SignUpFormInputs中的avatar改为可选类型,匹配未选择文件时的undefined值:
interface SignUpFormInputs { email: string; password: string; fullname: string; username: string; avatar?: File; // 改为可选类型 }
3. 保留现有Yup验证逻辑
你的Yup规则已经正确:required会在值为undefined时触发必填错误,自定义测试仅在文件存在时执行类型/大小验证,不会干扰必填规则。
调整后,未选择头像时会正常显示“Avatar is required”提示,选择文件后才会执行后续的文件格式和大小验证。
内容的提问来源于stack exchange,提问作者saribcantcode
相关产品推荐
相关产品推荐

