提问人:Abdelghani Rizki 提问时间:11/8/2023 更新时间:11/8/2023 访问量:10
下一个 JS,表单字段验证错误时未触发提交函数
Next JS, Submit Function Not Triggered on Form Field Validation Error
问:
我这个组件有一个问题:当其中一个表单字段出现错误时,“onSubmit”函数不会执行或被调用:
"use client";
import * as z from "zod";
import axios from "axios";
import { useState, useRef, useEffect } from "react";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import toast from "react-hot-toast";
import { CaretSortIcon } from "@/components/SVGs";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
} from "@/components/ui/command";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Customer } from "@/types";
import { cn } from "@/lib/utils";
import { CheckIcon } from "lucide-react";
import { countrys } from "@/components/countries";
import { Textarea } from "@/components/ui/textarea";
import useShoppingCart from "@/hooks/useShoppingCart";
import { Checkbox } from "@/components/ui/checkbox";
const formSchema = z.object({
billing_first_name: z
.string()
.min(2, { message: "Ce champ est obligatoire" }),
billing_last_name: z.string().min(2, { message: "Ce champ est obligatoire" }),
billing_company: z.string().optional(),
billing_address_1: z.string().min(5, { message: "Ce champ est obligatoire" }),
billing_address_2: z.string().optional(),
billing_city: z.string().min(2, { message: "Ce champ est obligatoire" }),
billing_postcode: z.string().min(3, { message: "Ce champ est obligatoire" }),
billing_country: z.string().min(2, { message: "Ce champ est obligatoire" }),
billing_email: z
.string()
.email({ message: "adresse e-mail invalide" })
.min(3, { message: "Ce champ est obligatoire" }),
billing_phone: z.string().min(2, { message: "Ce champ est obligatoire" }),
shipping_first_name: z
.string()
.min(2, { message: "Ce champ est obligatoire" }),
shipping_last_name: z
.string()
.min(2, { message: "Ce champ est obligatoire" }),
shipping_company: z.string().optional(),
shipping_address_1: z
.string()
.min(5, { message: "Ce champ est obligatoire" }),
shipping_address_2: z.string().optional(),
shipping_city: z.string().min(2, { message: "Ce champ est obligatoire" }),
shipping_postcode: z.string().min(3, { message: "Ce champ est obligatoire" }),
shipping_country: z.string().min(2, { message: "Ce champ est obligatoire" }),
notes: z.string().optional(),
});
type CheckoutValues = z.infer<typeof formSchema>;
interface CheckoutProps {
initialData: Customer;
}
const CheckoutForm: React.FC<CheckoutProps> = ({ initialData }) => {
const hiddenButtonRef = useRef<HTMLButtonElement | null>(null);
const [open_1, setOpen_1] = useState(false);
const [open_2, setOpen_2] = useState(false);
const [show, setShow] = useState(false);
const items = useShoppingCart((state) => state.items);
const shipping_metod = useShoppingCart((state) => state.shipping_method);
const tva_cost = useShoppingCart((state) => state.tva_cost);
const tva_rates = useShoppingCart((state) => state.tva_rates);
const submit = useShoppingCart((state) => state.submitCheckout);
const setSubmit = useShoppingCart((state) => state.setSubmitCheckout);
const submitSamuray = useShoppingCart((state) => state.submit);
const setSubmitSamuray = useShoppingCart((state) => state.setSubmit);
useEffect(() => {
if (hiddenButtonRef.current && submit === true) {
hiddenButtonRef.current.click();
}
}, [submit]);
const handleCheckboxChange = () => {
setShow(!show);
};
let updatedDefaultValues = {
billing_first_name: "",
billing_last_name: "",
billing_company: "",
billing_address_1: "",
billing_address_2: "",
billing_city: "",
billing_postcode: "",
billing_country: "",
billing_email: "",
billing_phone: "",
shipping_first_name: "",
shipping_last_name: "",
shipping_company: "",
shipping_address_1: "",
shipping_address_2: "",
shipping_city: "",
shipping_postcode: "",
shipping_country: "",
notes: "",
};
const form = useForm<CheckoutValues>({
resolver: zodResolver(formSchema),
defaultValues: updatedDefaultValues,
});
useEffect(() => {
// eslint-disable-next-line
updatedDefaultValues = initialData
? {
// Billing information
billing_first_name: initialData.billing.first_name,
billing_last_name: initialData.billing.last_name,
billing_company: initialData.billing.company,
billing_address_1: initialData.billing.address_1,
billing_address_2: initialData.billing.address_2,
billing_city: initialData.billing.city,
billing_postcode: initialData.billing.postcode,
billing_country: initialData.billing.country,
billing_email: initialData.billing.email,
billing_phone: initialData.billing.phone,
// Shipping information
shipping_first_name: show
? initialData.shipping.first_name
: initialData.billing.first_name,
shipping_last_name: show
? initialData.shipping.last_name
: initialData.billing.last_name,
shipping_company: show
? initialData.shipping.company
: initialData.billing.company,
shipping_address_1: show
? initialData.shipping.address_1
: initialData.billing.address_1,
shipping_address_2: show
? initialData.shipping.address_2
: initialData.shipping.address_2,
shipping_city: show
? initialData.shipping.city
: initialData.billing.city,
shipping_postcode: show
? initialData.shipping.postcode
: initialData.billing.postcode,
shipping_country: show
? initialData.shipping.country
: initialData.billing.country,
notes: "",
}
: {
billing_first_name: "",
billing_last_name: "",
billing_company: "",
billing_address_1: "",
billing_address_2: "",
billing_city: "",
billing_postcode: "",
billing_country: "",
billing_email: "",
billing_phone: "",
shipping_first_name: "",
shipping_last_name: "",
shipping_company: "",
shipping_address_1: "",
shipping_address_2: "",
shipping_city: "",
shipping_postcode: "",
shipping_country: "",
notes: "",
};
// console.log(
// `show: ${show}: ` + JSON.stringify(updatedDefaultValues, null, 4)
// );
form.reset(updatedDefaultValues);
}, [show, initialData]);
const onSubmit = async (data: CheckoutValues) => {
try {
// alert(JSON.stringify(data, null, 4));
const response = await axios.post("/api/payment", {
products: items,
checkout: data,
shipping_metod: shipping_metod,
tva_rates: tva_rates,
tva_cost: tva_cost,
});
window.location = response.data.url;
setSubmit(false);
} catch (error: any) {
toast.error(error.response?.data?.error || "An error occurred.");
} finally {
setSubmit(false);
}
};
return (
<>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="flex flex-col text-left w-full gap-y-8"
>
<div className="bg-white shadow-md flex flex-col text-left w-full gap-y-6 p-[24px_22px] border border-solid border-[#f5f4f7] rounded-lg">
<h4 className="text-black font-medium text-[16px] leading-[100%] tracking-[-0.04em] mb-[26px]">
Détails de facturation.
</h4>
<div className="w-full grid-cols-1 grid lg:grid-cols-2 gap-8">
{/* first Name */}
<FormField
control={form.control}
name="billing_first_name"
render={({ field }) => (
<FormItem>
<FormLabel>Prénom*</FormLabel>
<FormControl>
<Input
disabled={submit ? true : false}
placeholder="Entrez votre prénom..."
{...field}
type="text"
className="bg-[#f1f1f1] max-w-[calc(100%-12px)] mx-[auto] focus-visible:outline-[#dfb572]"
/>
</FormControl>
<FormMessage className="text-[#ea400e] text-[10px] p-[5px_0px_0px] !m-0" />
</FormItem>
)}
/>
{/* last Name */}
<FormField
control={form.control}
name="billing_last_name"
render={({ field }) => (
<FormItem>
<FormLabel>Nom de famille*</FormLabel>
<FormControl>
<Input
disabled={submit ? true : false}
placeholder="Entrez votre nom de famille..."
{...field}
type="text"
className="bg-[#f1f1f1] max-w-[calc(100%-12px)] mx-[auto] focus-visible:outline-[#dfb572]"
/>
</FormControl>
<FormMessage className="text-[#ea400e] text-[10px] p-[5px_0px_0px] !m-0" />
</FormItem>
)}
/>
</div>
{/* Company name (optional) */}
<FormField
control={form.control}
name="billing_company"
render={({ field }) => (
<FormItem>
<FormLabel>Nom de l'entreprise (facultatif)</FormLabel>
<FormControl>
<Input
disabled={submit ? true : false}
placeholder=""
{...field}
type="text"
className="bg-[#f1f1f1] max-w-[calc(100%-12px)] mx-[auto] focus-visible:outline-[#dfb572]"
/>
</FormControl>
<FormMessage className="text-[#ea400e] text-[10px] p-[5px_0px_0px] !m-0" />
</FormItem>
)}
/>
{/* Country */}
<FormField
control={form.control}
name="billing_country"
render={({ field }) => (
<FormItem className="flex flex-col ">
<FormLabel>Pays / Région *</FormLabel>
<Popover open={open_1} onOpenChange={setOpen_1}>
<PopoverTrigger asChild>
<FormControl>
<Button
disabled={submit ? true : false}
variant="outline"
role="combobox"
aria-expanded={open_1}
className={cn(
"w-[100%] justify-between bg-[#f1f1f1] max-w-[calc(100%-12px)] mx-[auto] focus-visible:outline-[#dfb572]",
!field.value && "text-muted-foreground"
)}
>
{field.value
? countrys.find(
(country) => country.value === field.value
)?.label
: "Sélectionner un pays / une région..."}
<CaretSortIcon />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-[200px] max-h-96 overflow-y-auto p-0 relative">
<Command>
<CommandInput
placeholder="Search country..."
className="h-9"
/>
<CommandEmpty>No country found.</CommandEmpty>
<CommandGroup>
{countrys.map((country) => (
<CommandItem
value={country.label}
key={country.value}
onSelect={() => {
form.setValue("billing_country", country.value);
setOpen_1(false);
}}
>
{country.label}
<CheckIcon
className={cn(
"ml-auto h-4 w-4",
country.value === field.value
? "opacity-100"
: "opacity-0"
)}
/>
</CommandItem>
))}
</CommandGroup>
</Command>
</PopoverContent>
</Popover>
<FormMessage className="text-[#ea400e] text-[10px] p-[5px_0px_0px] !m-0" />
</FormItem>
)}
/>
{/* Town / City * */}
<FormField
control={form.control}
name="billing_city"
render={({ field }) => (
<FormItem>
<FormLabel>Ville *</FormLabel>
<FormControl>
<Input
disabled={submit ? true : false}
placeholder="Entrez votre ville"
{...field}
type="text"
className="bg-[#f1f1f1] max-w-[calc(100%-12px)] mx-[auto] focus-visible:outline-[#dfb572]"
/>
</FormControl>
<FormMessage className="text-[#ea400e] text-[10px] p-[5px_0px_0px] !m-0" />
</FormItem>
)}
/>
{/* Street address */}
<FormField
control={form.control}
name="billing_address_1"
render={({ field }) => (
<FormItem>
<FormLabel>Adresse postale *</FormLabel>
<FormControl>
<Input
disabled={submit ? true : false}
placeholder="Numéro de maison et nom de la rue"
{...field}
type="text"
className="bg-[#f1f1f1] max-w-[calc(100%-12px)] mx-[auto] focus-visible:outline-[#dfb572]"
/>
</FormControl>
<FormMessage className="text-[#ea400e] text-[10px] p-[5px_0px_0px] !m-0" />
</FormItem>
)}
/>
{/* Street address */}
<FormField
control={form.control}
name="billing_address_2"
render={({ field }) => (
<FormItem>
{/* <FormLabel>Street address *</FormLabel> */}
<FormControl>
<Input
disabled={submit ? true : false}
placeholder="Appartement, suite, unité, etc. (facultatif)"
{...field}
type="text"
className="bg-[#f1f1f1] max-w-[calc(100%-12px)] mx-[auto] focus-visible:outline-[#dfb572]"
/>
</FormControl>
<FormMessage className="text-[#ea400e] text-[10px] p-[5px_0px_0px] !m-0" />
</FormItem>
)}
/>
{/* Postcode / ZIP * */}
<FormField
control={form.control}
name="billing_postcode"
render={({ field }) => (
<FormItem>
<FormLabel>Code postal / ZIP *</FormLabel>
<FormControl>
<Input
disabled={submit ? true : false}
placeholder="Entrez la code postal de votre ville"
{...field}
type="text"
className="bg-[#f1f1f1] max-w-[calc(100%-12px)] mx-[auto] focus-visible:outline-[#dfb572]"
/>
</FormControl>
<FormMessage className="text-[#ea400e] text-[10px] p-[5px_0px_0px] !m-0" />
</FormItem>
)}
/>
{/* Phone */}
<FormField
control={form.control}
name="billing_phone"
render={({ field }) => (
<FormItem>
<FormLabel>Téléphone *</FormLabel>
<FormControl>
<Input
disabled={submit ? true : false}
placeholder="Entrez votre téléphone"
{...field}
type="text"
className="bg-[#f1f1f1] max-w-[calc(100%-12px)] mx-[auto] focus-visible:outline-[#dfb572]"
/>
</FormControl>
<FormMessage className="text-[#ea400e] text-[10px] p-[5px_0px_0px] !m-0" />
</FormItem>
)}
/>
{/* Email address */}
<FormField
control={form.control}
name="billing_email"
render={({ field }) => (
<FormItem>
<FormLabel>Adresse e-mail *</FormLabel>
<FormControl>
<Input
disabled={submit ? true : false}
placeholder="Entrez votre adresse e-mail"
{...field}
type="email"
className="bg-[#f1f1f1] max-w-[calc(100%-12px)] mx-[auto] focus-visible:outline-[#dfb572]"
/>
</FormControl>
<FormMessage className="text-[#ea400e] text-[10px] p-[5px_0px_0px] !m-0" />
</FormItem>
)}
/>
</div>
<Button type="submit" className={"hidden"} ref={hiddenButtonRef}>
Continuer vers le paiement
</Button>
</form>
</Form>
</>
);
};`
export default CheckoutForm;
我希望当表单提交时,onSubmit 将被调用,如果表单上有任何错误,将执行“setSubmit(false)”,而无需向 api 发送任何请求
答: 暂无答案
评论