如何在react-hook-form的handleSubmit中同步执行自定义验证?
解决React Hook Form中自定义验证与内置验证错误同时显示的问题
你可以通过两种方式实现自定义验证与yup内置验证错误的同时显示,核心是将自定义验证的错误整合到React Hook Form的errors对象中,或者把自定义验证逻辑融入yup的schema里。
方案一:手动控制验证流程,统一管理错误
通过setError将自定义验证错误添加到表单错误集合,同时用trigger触发所有字段的内置验证,确保两类错误同时被渲染:
import { useState } from "react"; import { useForm } from "react-hook-form"; import { yupResolver } from "@hookform/resolvers/yup"; import * as yup from "yup"; // 原有的yup验证schema const validationFormSchema = yup.object().shape({ name: yup.string().required("请输入产品名称"), }); const Form = () => { const [selectedImages, setSelectedImages] = useState([]); const formOptions = { resolver: yupResolver(validationFormSchema) }; const { register, formState: { errors }, setError, clearErrors, trigger, getValues, } = useForm(formOptions); const handleAddImg = (e) => { const fileListArrayImg = Array.from(e.target.files); setSelectedImages(fileListArrayImg); }; // 自定义图片验证函数,返回错误信息(无错误则返回null) const validateImgs = (images) => { if (images.length === 0) return "请至少选择一张图片"; for (const img of images) { if (img.size > 2 * 1024 * 1024) { return `图片${img.name}大小不能超过2MB`; } if (!["image/png", "image/jpeg"].includes(img.type)) { return `图片${img.name}格式必须为PNG或JPEG`; } } return null; }; const customSubmit = async () => { // 清除之前的图片错误 clearErrors("image"); // 执行图片验证 const imgErrorMsg = validateImgs(selectedImages); if (imgErrorMsg) { setError("image", { message: imgErrorMsg }); } // 触发所有字段的内置yup验证 const isFormValid = await trigger(); // 只有两类验证都通过,才执行提交逻辑 if (isFormValid && !imgErrorMsg) { const formData = getValues(); // 这里写你的提交逻辑 console.log("提交表单数据:", formData, selectedImages); } }; return ( <form encType="multipart/form-data" onSubmit={(e) => { e.preventDefault(); customSubmit(); }} > <input type="text" {...register("name")} placeholder="产品名称" /> <span style={{ color: "red" }}>{errors.name?.message}</span> <input type="file" multiple accept="image/png, image/jpeg" onChange={handleAddImg} /> <span style={{ color: "red" }}>{errors.image?.message}</span> <button type="submit">提交</button> </form> ); }; export default Form;
方案二:将自定义验证整合到Yup Schema中(推荐)
把图片验证逻辑直接写到yup的schema里,让yupResolver统一处理所有验证,无需手动控制提交流程:
import { useState } from "react"; import { useForm } from "react-hook-form"; import { yupResolver } from "@hookform/resolvers/yup"; import * as yup from "yup"; const Form = () => { const [selectedImages, setSelectedImages] = useState([]); // 整合了图片验证的yup schema const validationFormSchema = yup.object().shape({ name: yup.string().required("请输入产品名称"), image: yup.array().test( "image-validation", "", (value) => { if (value.length === 0) { return new yup.ValidationError("请至少选择一张图片", value, "image"); } for (const img of value) { if (img.size > 2 * 1024 * 1024) { return new yup.ValidationError( `图片${img.name}大小不能超过2MB`, value, "image" ); } if (!["image/png", "image/jpeg"].includes(img.type)) { return new yup.ValidationError( `图片${img.name}格式必须为PNG或JPEG`, value, "image" ); } } return true; } ), }); const formOptions = { resolver: yupResolver(validationFormSchema), defaultValues: { name: "", image: [] }, }; const { register, handleSubmit, formState: { errors }, setValue, } = useForm(formOptions); const handleAddImg = (e) => { const fileListArrayImg = Array.from(e.target.files); setSelectedImages(fileListArrayImg); // 将选中的图片同步到表单字段中,供yup验证 setValue("image", fileListArrayImg); }; const submitFunction = async (formData) => { // 此时所有验证已通过,直接执行提交逻辑 console.log("提交表单数据:", formData); }; return ( <form encType="multipart/form-data" onSubmit={handleSubmit(submitFunction)}> <input type="text" {...register("name")} placeholder="产品名称" /> <span style={{ color: "red" }}>{errors.name?.message}</span> <input type="file" multiple accept="image/png, image/jpeg" onChange={handleAddImg} /> <span style={{ color: "red" }}>{errors.image?.message}</span> <button type="submit">提交</button> </form> ); }; export default Form;
说明
- 两种方案都能让yup内置验证错误和自定义图片验证错误同时显示,因为所有错误都被整合到React Hook Form的
errors对象中。 - 方案二更贴合React Hook Form与yup的集成设计,将所有验证逻辑集中管理,代码更简洁。
- 你之前尝试的写法问题在于:自定义验证的错误没有被添加到表单的
errors集合中,且handleSubmit仍会优先执行内置验证,导致两类错误无法同时触发。
内容的提问来源于stack exchange,提问作者Fernando
相关产品推荐
相关产品推荐

