Makala haya yamefadhiliwa na SurveyJS Kuna mtindo wa kiakili ambao watengenezaji wengi wa React hushiriki bila hata kuujadili kwa sauti. Fomu hizo daima zinatakiwa kuwa vipengele. Hii inamaanisha mkusanyiko kama:

React Hook Form kwa jimbo la karibu (re-renders ndogo, usajili wa uwanja wa ergonomic, mwingiliano wa lazima). Zod kwa uthibitisho (usahihi wa pembejeo, uthibitishaji wa mipaka, uchanganuzi wa aina-salama). React Query kwa backend: kuwasilisha, kujaribu tena, akiba, kusawazisha server, na kadhalika.

Na kwa idadi kubwa ya aina - skrini zako za kuingia, kurasa zako za mipangilio, muundo wako wa CRUD - hii inafanya kazi vizuri sana. Kila kipande hufanya kazi yake, wanatunga kwa usafi, na unaweza kuendelea na sehemu za programu yako ambazo hutofautisha bidhaa yako. Lakini kila baada ya muda fulani, fomu huanza kukusanya vitu kama vile sheria za mwonekano ambazo zinategemea majibu ya awali, au maadili yanayotokana ambayo hupitia sehemu tatu. Labda hata kurasa zote ambazo zinapaswa kurukwa au kuonyeshwa kulingana na jumla inayoendeshwa. Unashughulikia masharti ya kwanza na useWatch na tawi la ndani, ambayo ni sawa. Kisha mwingine. Kisha unatafuta superRefine ili kusimba sheria za sehemu mbalimbali ambazo utaratibu wako wa Zod hauwezi kueleza kwa njia ya kawaida. Kisha, urambazaji wa hatua huanza kuvuja mantiki ya biashara. Wakati fulani, unatazama ulichounda na kugundua kuwa fomu hiyo si UI tena. Ni zaidi ya mchakato wa uamuzi, na mti wa sehemu ni mahali ambapo ulifanyika kuihifadhi. Hapa ndipo nadhani mtindo wa kiakili wa fomu katika React huvunjika, na kwa kweli hakuna kosa la mtu yeyote. Rafu ya RHF + Zod ni bora kwa kile iliundwa. Suala ni kwamba huwa tunaendelea kuitumia kupita mahali ambapo vifupisho vyake vinalingana na shida kwa sababu njia mbadala inahitaji njia tofauti ya kufikiria juu ya fomu kabisa. Makala hii inahusu mbadala huo. Ili kuonyesha hili, tutaunda fomu sawa ya hatua nyingi mara mbili:

Na Fomu ya React Hook + Zod iliyounganishwa ili Kujibu Hoja kwa uwasilishaji, Na SurveyJS, ambayo huchukulia fomu kama data - schema rahisi ya JSON - badala ya mti wa sehemu.

Mahitaji sawa, mantiki sawa ya masharti, simu sawa ya API mwishoni. Kisha tutapanga ramani ni nini hasa kilichosogezwa na kilichobaki, na kuweka njia ya vitendo ya kuamua ni mtindo gani unapaswa kutumia, na wakati gani. Fomu tunayotengeneza:

Fomu hii itatumia mtiririko wa hatua 4: Hatua ya 1: Maelezo

Jina la kwanza (linahitajika), Barua pepe (inahitajika, umbizo halali).

Hatua ya 2: Agizo

Bei ya kitengo, Kiasi, Kiwango cha ushuru, Imetolewa: Jumla ndogo, Kodi, Jumla.

Hatua ya 3: Akaunti na Maoni

Je, una akaunti? (Ndiyo/Hapana) Ikiwa Ndiyo → jina la mtumiaji + nenosiri, zote zinahitajika. Ikiwa Hapana → barua pepe tayari imekusanywa katika hatua ya 1.

Ukadiriaji wa kuridhika (1–5) Ikiwa ≥ 4 → uliza "Ulipenda nini?" Ikiwa ≤ 2 → uliza "Tunaweza kuboresha nini?"

Hatua ya 4: Kagua

Inaonekana tu ikiwa jumla >= 100 Uwasilishaji wa mwisho.

Hii sio kali. Lakini inatosha kufichua tofauti za usanifu. Sehemu ya 1: Inayoendeshwa na Kipengele (Aina ya React Hook + Zod) Ufungaji npm sakinisha react-hook-form zod @hookform/resolvers @tanstack/react-query

Zod Schema Hebu tuanze na schema ya Zod, kwa sababu ni kawaida ambapo sura ya fomu huanzishwa. Kwa hatua mbili za kwanza - maelezo ya kibinafsi na pembejeo za kuagiza - kila kitu ni moja kwa moja: mifuatano inayohitajika, nambari zilizo na kiwango cha chini, na enum. Sehemu ya kuvutia huanza unapojaribu kueleza sheria za masharti.

kuagiza {z } kutoka "zod";

export const formSchema = z.object({ firstName: z.string().min(1, "Inahitajika"), barua pepe: z.string().barua pepe("Barua pepe batili"), bei: z.number().min(0), kiasi: z.number().min(1), taxRate: z.number(), ina "Yescount:Acnum" z.string().hiari(), nenosiri: z.string().hiari(), kuridhika: z.number().min(1).max(5), positiveFeedback: z.string().optional(), improvementFeedback: z.string().optional(),}).superRefine((data, ctx) => { if (data) =Yeus =Yeus =Yeus. {ctx.addIssue({code: "desturi", njia: ["jina la mtumiaji"], ujumbe: "Inahitajika" }} ikiwa (!data.password || data.password.length < 6) { ctx.addIssue({code: "custom", path: ["password"]), herufi: "}}};

ikiwa (data.satisfaction >= 4 && !data.positiveFeedback) {ctx.addIssue({code: "custom", path: ["positiveFeedback"], ujumbe: "Tafadhali shiriki ulichopenda" }); }

ikiwa (data.satisfaction <= 2 && !data.improvementFeedback) {ctx.addIssue({code: "custom", path:["improvementFeedback"], ujumbe: "Tafadhali tuambie la kuboresha" }); }});

aina ya kuuza nje FormData = z.infer;

Tambua kuwa jina la mtumiaji na nenosiri zimechapishwa kama hiari() ingawa zinahitajika kwa masharti kwa sababu taratibu za kiwango cha aina za Zod hufafanua umbo la kitu, si sheria zinazotawala wakati sehemu ni muhimu. Sharti la masharti lazima liishi ndani ya superRefine, ambayo huendesha baada ya umbo kuthibitishwa na kupata kitu kamili. Utengano huo sio dosari; ni kile ambacho chombo kimeundwa kwa ajili yake: superRefine ni mahali ambapo mantiki ya sehemu-msingi huenda wakati haiwezi kuonyeshwa katika muundo wa schema yenyewe. Kinachojulikana pia hapa ni kile ambacho schema hii haionyeshi. Haina dhana ya kurasa, hakuna dhana ya ni nyanja gani zinaonekana kwa wakati gani, na hakuna dhana ya urambazaji. Yote hayo yataishi mahali pengine. Sehemu ya Fomu

leta {useForm, useWatch } kutoka "react-hook-form";leta {zodResolver } kutoka "@hookform/resolvers/zod"; leta {useMutation } kutoka "@tanstack/react-query"; leta {useState, useMemo } kutoka "react"; leta {formSchema} kutoka kwa "Daschema";

const STEPS = ["maelezo", "agiza", "akaunti", "hakiki"];

chapa OrderPayload = FormData & { subtotal: number; ushuru: nambari; jumla: nambari };

kazi ya kuuza nje RHFMultiStepForm() { const [hatua, setStep] = useState(0);

const mutation = useMutation ({ mutationFn: async (mzigo wa malipo: OrderPayload) => { const res = subiri kuchota("/api/orders", { njia: "POST", vichwa: { "Content-Type": "application/json" }, mwili: JSON.stringify(payload), }); ikiwa (!res.ok) kutupa Hitilafu mpya("Imeshindwa kuwasilisha"); rudisha res.json(); }, });

const { register, control, handleSubmit, formState: { errors }, } = useForm({solver: zodResolver(formSchema), defaultValues: {bei: 0, quantity: 1, taxRate: 0.1, satisfaction: 3, hasAccount: "No);", },}} const price = useWatch({ control, name: "bei" }); const quantity = useWatch({ control, name: "quantity" }); const taxRate = useWatch({ control, name: "taxRate" }); const hasAccount = useWatch({ control, name: "hasAccount" }); const satisfaction = useWatch({kudhibiti, jina: "kuridhika" }); const subtotal = useMemo(() => (bei ?? 0) * (idadi ?? 1), [bei, kiasi]); const tax = useMemo(() => jumla ndogo * (Kiwango cha kodi ?? 0), [jumla ndogo, Kiwango cha kodi]); const total = useMemo(() => jumla ndogo + kodi, [jumla ndogo, kodi]); const onSubmit = (data: FormData) => mutation.mutate({ ...data, subtotal, tax, total }); const showSubmit = (hatua === 2 && jumla < 100) || (hatua === 3 && jumla >= 100)

rudisha (

{step === 0 && ( <> )}

{step === 1 && ( <> 5%

Jumla ndogo: {subtotal}
Kodi: {tax}
Jumla: {jumla}
)}

{step === 2 && ( <>

{hasAccount === "Ndiyo" && ( <> )}

{satisfaction >= 4 && (