Mae'r erthygl hon yn cael ei noddi gan SurveyJS Mae yna fodel meddwl y mae'r rhan fwyaf o ddatblygwyr React yn ei rannu heb erioed ei drafod yn uchel. Mae'r ffurflenni hynny bob amser i fod i fod yn gydrannau. Mae hyn yn golygu pentwr fel:

Ffurflen React Hook ar gyfer gwladwriaeth leol (ail-rendradau lleiaf posibl, cofrestru maes ergonomig, rhyngweithio hanfodol). Zod ar gyfer dilysu (cywirdeb mewnbwn, dilysu ffiniau, dosrannu math-diogel). Ymateb Ymholiad ar gyfer backend: cyflwyno, ail-geisio, caching, cysoni gweinydd, ac ati.

Ac ar gyfer y mwyafrif helaeth o ffurflenni - eich sgriniau mewngofnodi, eich tudalennau gosodiadau, eich moddau CRUD - mae hyn yn gweithio'n dda iawn. Mae pob darn yn gwneud ei waith, maen nhw'n cyfansoddi'n lân, a gallwch chi symud ymlaen i'r rhannau o'ch cais sy'n gwahaniaethu'ch cynnyrch mewn gwirionedd. Ond o bryd i'w gilydd, mae ffurflen yn dechrau cronni pethau fel rheolau gwelededd sy'n dibynnu ar atebion cynharach, neu werthoedd deilliedig sy'n rhaeadru trwy dri maes. Efallai hyd yn oed tudalennau cyfan y dylid eu hepgor neu eu dangos yn seiliedig ar gyfanswm rhedeg. Rydych chi'n trin yr amodol cyntaf gyda useWatch a changen unol, sy'n iawn. Yna un arall. Yna rydych chi'n estyn am superRefine i amgodio rheolau traws-faes na all eich sgema Zod eu mynegi yn y ffordd arferol. Yna, mae llywio cam yn dechrau gollwng rhesymeg busnes. Ar ryw adeg, rydych chi'n edrych ar yr hyn rydych chi wedi'i adeiladu ac yn sylweddoli nad yw'r ffurflen yn UI bellach mewn gwirionedd. Mae’n fwy o broses benderfynu, a’r goeden gydran yn union lle y digwyddoch ei storio. Dyma lle dwi’n meddwl bod y model meddyliol ar gyfer ffurflenni yn React yn chwalu, a bai neb mewn gwirionedd. Mae stac RHF + Zod yn ardderchog ar yr hyn y cafodd ei ddylunio ar ei gyfer. Y mater yw ein bod yn tueddu i barhau i'w ddefnyddio heibio'r pwynt lle mae ei dyniadau yn cyd-fynd â'r broblem oherwydd bod y dewis arall yn gofyn am ffordd wahanol o feddwl am ffurflenni yn gyfan gwbl. Mae'r erthygl hon yn ymwneud â'r dewis arall hwnnw. I ddangos hyn, byddwn yn adeiladu'r un ffurf aml-gam yn union ddwywaith:

Gyda Ffurflen Hook React + Zod wedi'i wifro i React Query i'w gyflwyno, Gyda SurveyJS, sy'n trin ffurf fel data - sgema JSON syml - yn hytrach na choeden gydran.

Yr un gofynion, yr un rhesymeg amodol, yr un alwad API ar y diwedd. Yna byddwn yn mapio’n union beth symudodd a beth arhosodd, a gosod allan ffordd ymarferol o benderfynu pa fodel y dylech ei ddefnyddio, a phryd. Y ffurflen rydyn ni'n ei hadeiladu:

Bydd y ffurflen hon yn defnyddio llif 4 cam: Cam 1: Manylion

Enw cyntaf (gofynnol), E-bost (gofynnol, fformat dilys).

Cam 2: Gorchymyn

Pris uned, Nifer, Cyfradd treth, Yn deillio: Is-gyfanswm, Treth, Cyfanswm.

Cam 3: Cyfrif ac Adborth

Oes gennych chi gyfrif? (Ie/Na) Os Oes → enw defnyddiwr + cyfrinair, mae angen y ddau. Os Na → e-bost a gasglwyd eisoes yng ngham 1.

Sgôr boddhad (1-5) Os ≥ 4 → gofynnwch “Beth oeddech chi'n ei hoffi?” Os ≤ 2 → gofynnwch “Beth allwn ni ei wella?”

Cam 4: Adolygu

Dim ond yn ymddangos os yw'r cyfanswm >= 100 Cyflwyniad terfynol.

Nid yw hyn yn eithafol. Ond mae'n ddigon i ddatgelu gwahaniaethau pensaernïol. Rhan 1: A yrrir gan Gydran (Ffurflen Adwaith Bachyn + Zod) Gosodiad npm gosod zod react-hook-form @hookform/resolvers @tanstack/react-query

Sgema Zod Gadewch i ni ddechrau gyda sgema Zod, oherwydd dyna fel arfer lle mae siâp y ffurflen yn sefydlu. Ar gyfer y ddau gam cyntaf - manylion personol a mewnbynnau archeb - mae popeth yn syml: llinynnau gofynnol, rhifau ag isafswm, ac enum. Mae'r rhan ddiddorol yn dechrau pan fyddwch chi'n ceisio mynegi'r rheolau amodol.

mewnforio { z } o "zod";

allforio const formSchema = z.object({ firstName: z.string().min(1, "Angenrheidiol"), e-bost: z.string().email("E-bost annilys"), pris: z.number().min(0), maint: z.number().min(1), taxRate: z.number(), hasAccount: z.(number(), hasAccount: z.(). cyfrinair: z.string().optional(), boddhad: z.number().min(1).max(5), positiveAdborth: z.string().optional(), improvementFeedback: z.string().optional(),}).superRefine((data, ctx) => {os (data.hasAccount === "Ie") {os (!) { os yw (!) cod enw (!) { os (!) {. "custom", path: [ "enw defnyddiwr"], neges: "Angenrheidiol" }); } os (!data.password || data.password.length < 6) { ctx.addIssue({ code: "custom", path: ["cyfrinair"], neges: "Min 6 nod" });

os (data.satisfaction >= 4 && !data.positiveFeedback) { ctx.addIssue({ code: "custom", path: ["positiveFeedback"], neges: "Rhannwch yr hyn yr oeddech yn ei hoffi" }); }

os (data.satisfaction <= 2 && !data.improvementFeedback) { ctx.addIssue({ cod: "custom", llwybr:["improvementFeedback"], message: "Dywedwch wrthym beth i'w wella" }); }});

allforio math FormData = z.infer ;

Sylwch fod enw defnyddiwr a chyfrinair yn cael eu teipio fel dewisol () er bod eu hangen yn amodol oherwydd bod sgema lefel math Zod yn disgrifio siâp y gwrthrych, nid y rheolau sy'n llywodraethu pan fo meysydd yn bwysig. Mae'n rhaid i'r gofyniad amodol fyw y tu mewn i superRefine, sy'n rhedeg ar ôl i'r siâp gael ei ddilysu a chael mynediad i'r gwrthrych llawn. Nid diffyg yw'r gwahaniad hwnnw; dyma'r union beth y mae'r offeryn wedi'i gynllunio ar ei gyfer: superRefine yw lle mae rhesymeg traws-faes yn mynd pan na ellir ei mynegi yn strwythur y sgema ei hun. Yr hyn sydd hefyd yn nodedig yma yw'r hyn nad yw'r sgema hwn yn ei fynegi. Nid oes ganddo unrhyw gysyniad o dudalennau, dim cysyniad o ba feysydd sy'n weladwy ar ba bwynt, a dim cysyniad o lywio. Bydd hynny i gyd yn byw yn rhywle arall. Cydran Ffurf

mewnforio { useForm, useWatch } o "react-hook-form"; mewnforio { zodResolver } o "@hookform/resolvers/zod"; mewnforio { useMutation } o "@tanstack/react-query"; mewnforio { useState, useMemo } o "React"; teipio {formSemach };

const STEPS = [ "manylion", "archeb", "cyfrif", "adolygiad"];

type OrderPayload = FormData &{ subtotal: number; treth : rhif ; cyfanswm: number };

ffwythiant allforio RHFMultiStepForm() { const [step, setStep] = useState(0);

treiglad const = useMutation({ treigladFn: async (llwyth tâl: OrderPayload) => { const res = aros nôl ("/api/orders", { dull: "POST", penawdau: { "Content-Math" : "cais/json" }, corff: JSON.stringify(llwyth cyflog), }); os (!res.ok) taflu Gwall newydd("Methwyd cyflwyno"); dychwelyd res.json(); }, });

const { cofrestru, rheoli, handleSubmit, formState: { gwallau }, } = useForm ({ resolver: zodResolver(formSchema), defaultValues: { pris: 0, maint: 1, trethCyfradd: 0.1, boddhad: 3, hasAccount: "Na", }, }); const price = useWatch({ rheolaeth, enw: "pris" }); const size = useWatch({ rheolaeth, enw: "swm" }); const taxRate = useWatch({ control, name: "taxRate" }); const hasAccount = useWatch({ control, name: "hasAccount" }); boddhad const = useWatch({ rheolaeth, enw: "boddhad" }); const subtotal = useMemo(() => (pris ?? 0) * (swm ?? 1), [pris, maint]); treth const = useMemo(() => is-gyfanswm * (Cyfradd dreth ?? 0), [is-gyfanswm, Cyfradd dreth]); cyfanswm const = useMemo(() => is-gyfanswm + treth, [is-gyfanswm, treth]); const onSubmit = (data: FormData) => mutation.mutate({ ...data, is-gyfanswm, treth, cyfanswm }); const showSubmit = (cam === 2 && cyfanswm < 100) || (cam === 3 && cyfanswm >= 100)

dychwelyd (

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

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

Is-gyfanswm: {subtotal}
Treth: {tax}
Cyfanswm: {total}
)}

{step === 2 && ( <> )

{hasAccount === "Ie" && ( <> < mewnbwn {...register("username")} placeholder="Enw defnyddiwr" /> )}

{boddhad >= 4 && (