Reputation: 21
I have this component, component doesn't give any error but when I tried to submit my form onSubmit function is not triggered.
I used react-hook-form to create my form and I used shadcn UI components inside form. When I Click button which has type submit does not trigger onSubmit function which is called inside of handleSubmit. Why onSubmit is not triggered?
'use client';
import * as z from 'zod';
import { useState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { useFieldArray, useForm } from 'react-hook-form';
import { Trash } from 'lucide-react';
import { useRouter } from 'next/navigation';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage
} from '@/components/ui/form';
import { Separator } from '@/components/ui/separator';
import { Heading } from '@/components/ui/heading';
import toast from 'react-hot-toast';
import { AlertModal } from '../modal/alert-modal';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../ui/select';
import { Product } from '@prisma/client';
import { createDailyOrder, deleteDailyOrder } from '@/actions/daily-order-action';
const formSchema = z.object({
date: z.string().min(1, { message: 'Date is required' }),
dailyOrders: z.array(z.object({
id: z.string().min(1, { message: 'Id is required' }),
productId: z.string().min(1, { message: 'Product is required' }),
quantity: z.coerce.number().min(1, { message: 'Quantity is required' }),
}))
});
export type DailyOrderFormValues = z.infer<typeof formSchema>;
interface DailyOrderFormProps {
initialData: any | null;
products: Product[]
}
export const DailyOrderForm: React.FC<DailyOrderFormProps> = ({
initialData,
products
}) => {
const router = useRouter();
const [open, setOpen] = useState(false);
const [loading, setLoading] = useState(false);
const title = initialData ? 'Edit Daily Order' : 'Create Daily Order';
const description = initialData ? 'Edit Daily Order' : 'Add a new Daily Order';
const toastMessage = initialData ? 'DailyOrder updated.' : 'DailyOrder created.';
const action = initialData ? 'Save changes' : 'Create';
const defaultValues = initialData
? {
date: initialData.date, dailyOrders: initialData.productsData
}
: {
date: '', dailyOrders: [{ id: '1', productId: '', quantity: 0 }]
}
const form = useForm<DailyOrderFormValues>({
resolver: zodResolver(formSchema),
defaultValues
});
const { fields, append, remove, } = useFieldArray({
control: form.control,
name: 'dailyOrders'
})
const onSubmit = async (data: DailyOrderFormValues) => {
try {
setLoading(true);
await createDailyOrder(data, initialData);
toast.success(toastMessage);
router.push(`/daily-orders`);
} catch (error: any) {
toast.error(error.message);
} finally {
setLoading(false);
}
};
const onDelete = async () => {
try {
setLoading(true);
router.refresh();
await deleteDailyOrder(initialData.id);
toast.success('DailyOrder deleted.');
router.push(`/daily-orders`);
} catch (error: any) {
toast.error(error.message);
} finally {
setLoading(false);
setOpen(false);
}
};
return (
<>
<AlertModal
isOpen={open}
onClose={() => setOpen(false)}
onConfirm={onDelete}
loading={loading}
/>
<div className="flex items-center justify-between">
<Heading title={title} description={description} />
{initialData && (
<Button
disabled={loading}
variant="destructive"
size="sm"
onClick={() => setOpen(true)}
>
<Trash className="h-4 w-4" />
</Button>
)}
</div>
<Separator />
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="w-full space-y-8"
>
<div className="gap-8 md:grid md:grid-cols-5">
<FormField
control={form.control}
name="date"
render={({ field }) => (
<FormItem>
<FormLabel>Date</FormLabel>
<FormControl>
<Input type="date" disabled={loading} {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button
className='mt-8'
type="button"
onClick={() => {
append({ id: `${fields.length + 1}`, productId: '', quantity: 0 });
}}
>Add
</Button>
</div>
<div>
{fields.map((item, index) => (
<div className='flex gap-4' key={item.id}>
<div className='min-w-60'>
<FormField
control={form.control}
name={`dailyOrders.${index}.productId`}
render={({ field }) => {
return (
<FormItem>
<FormLabel>Product</FormLabel>
<Select
disabled={loading}
onValueChange={field.onChange}
value={field.value}
defaultValue={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue
defaultValue={field.value}
placeholder="Select a product"
/>
</SelectTrigger>
</FormControl>
<SelectContent>
{products.map((product) => (
<SelectItem key={product.id} value={product.id}>
{product.name}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)
}}
/>
</div>
<FormField
control={form.control}
name={`dailyOrders.${index}.quantity`}
render={({ field }) => (
<FormItem>
<FormLabel>Quantity</FormLabel>
<FormControl>
<Input type="number" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button
className='mt-8'
variant={'destructive'}
type="button"
onClick={() => {
if (index === 0) return;
remove(index);
}}
>
<Trash className="h-4 w-4" />
</Button>
</div>
))}
</div>
<Button onClick={() => { console.log(form.formState.errors) }} disabled={loading} className="ml-auto" type="submit">
{action}
</Button>
</form>
</Form >
</>
);
};
I tried to log the errors in the console and from form errors there are no logs but it works on adding new data with this form.
Upvotes: 2
Views: 71
Reputation: 3678
onSubmit
won't trigger if you're missing form components that you specified in your formSchema. In your formSchema you specified dailyOrders as having 3 parts, but you only provided two FormFields. You're missing a FormField with name={`dailyOrders.${index}.quantity`}
Upvotes: 0