Reputation: 2691
I have a form with 3 fields, textbox, textarea and a file input. Textbox and textarea are required fields and file input is optional.
I am using react-hook-form, zod and zodResolver.
I added a scheme and set file input as optional in the schema, but when ever I submit the form without selecting a file, its showing error.
I want to show the validations for file input only if a file is selected (ie file input is optional)
'use client'
import { z } from "zod";
import { useForm } from 'react-hook-form'
import { zodResolver } from "@hookform/resolvers/zod";
export default function Form() {
const MAX_FILE_SIZE = 2000000
const ACCEPTED_IMAGE_TYPES = [
'image/jpeg',
'image/jpg',
'image/png',
'image/webp',
]
const imageSchema = z.any()
// To not allow empty files
.refine((files) => files?.length >= 1, { message: 'Photo is required.' })
// To not allow files other than images
.refine((files) => ACCEPTED_IMAGE_TYPES.includes(files?.[0]?.type), {
message: '.jpg, .jpeg, .png and .webp files are accepted.',
})
// To not allow files larger than 5MB
.refine((files) => files?.[0]?.size <= MAX_FILE_SIZE, {
message: `Max file size is 2MB.`,
}).optional()
const baseSchema = z.object({
title: z.string().min(1, 'Title required').max(60, 'Maximum 60 characters.'),
message: z.string().min(1, 'Message required.').max(120, 'Maximum 120 characters.'),
image: imageSchema,
});
type FormValues = z.infer<typeof baseSchema>;
const { register, handleSubmit, control, watch, reset, formState: { errors, isSubmitting } } = useForm<FormValues>({
resolver: zodResolver(baseSchema),
});
//submit form
const onSubmit = async (data: any) => {
console.log(data);
}
return (
<div className="mx-auto max-w-md container">
<form onSubmit={handleSubmit(onSubmit)}>
<div className="py-8 p-10 bg-slate-100 border-slate-200 rounded-xl border-2">
<div className="mb-6">
<label className="text-gray-700 font-bold" htmlFor="name">Title</label>
<input type="text" {...register('title')} className="w-full border border-gray-300 py-2 pl-3 rounded mt-2 outline-none" />
{errors.title && <div className="text-red-500">{errors.title?.message}</div>}
</div>
<div className="mb-6">
<div className="mb-6">
<label className="text-gray-700 font-bold" htmlFor="name">Message</label>
<textarea rows={4} {...register('message')} className="w-full border border-gray-300 py-2 pl-3 rounded mt-2 outline-none" />
{(errors as any).message && <div className="text-red-500">{(errors as any).message?.message}</div>}
</div>
</div>
<div className="mb-6">
<label className="text-gray-700 font-bold" htmlFor="name">Photo <span className="text-sm font-normal text-gray-700"> (optional)</span> </label>
<input type="file" {...register('image')} className="w-full border border-gray-300 py-2 pl-3 rounded mt-2 outline-none" />
{errors.image && <div className="text-red-500">{errors.image?.message?.toString()}</div>}
</div>
<button disabled={isSubmitting} type="submit" className="disabled:opacity-50 w-full mt-6 text-indigo-50 font-bold bg-indigo-600 py-3 rounded-md hover:bg-indigo-500"><span> Submit</span></button>
</div>
</form>
</div>
)
}
Upvotes: 0
Views: 5082
Reputation: 2691
The issue is in the imageSchema. I updated the image schema and its working as expected
const imageSchema = z.any().optional()
.refine(file => file.length == 1 ? ACCEPTED_IMAGE_TYPES.includes(file?.[0]?.type) ? true : false : true, 'Invalid file. choose either JPEG or PNG image')
.refine(file => file.length == 1 ? file[0]?.size <= MAX_FILE_SIZE ? true : false : true, 'Max file size allowed is 8MB.')
Upvotes: 2