Pawan Rijal
Pawan Rijal

Reputation: 21

Submit not working in react hook form in editing

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

Answers (1)

Jasperan
Jasperan

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

Related Questions