Saa asɛm yi yɛ nea SurveyJS na ɛboaa There’s a mental model dodow no ara React developers kyɛ a wɔnka ho asɛm denden da. Sɛ wɔkyerɛ sɛ nsusuwii ahorow yɛ nneɛma a ɛka bom bere nyinaa. Eyi kyerɛ sɛ stack te sɛ:

React Hook Form ma mpɔtam hɔ ɔman (minimal re-renders, ergonomic field registration, nkitahodi a ɛho hia). Zod ma validation (input correctness, hyeɛ validation, type-safe parsing). React Query ma backend: submission, san bɔ mmɔden, caching, server sync, ne nea ɛkeka ho.

Na wɔ nkrataa dodow no ara — wo login screens, wo nhyehyɛe nkratafa, wo CRUD modals — eyi yɛ adwuma yiye ankasa. Afã biara yɛ n’adwuma, wɔhyehyɛ no yiye, na wubetumi akɔ wo application no afã horow a ɛma wo nneɛma no da nsow ankasa no so. Nanso bere ne bere mu no, kratasin bi fi ase boaboa nneɛma te sɛ mmara a ɛfa sɛnea wotumi hu ade a egyina mmuae a wɔadi kan de ama so, anaasɛ gyinapɛn ahorow a wonya fi mu a ɛsen fa mfuw abiɛsa mu ano. Ebia mpo nkratafa mũ nyinaa a ɛsɛ sɛ wohuruw anaasɛ wɔkyerɛ a egyina running total so. Wode useWatch ne inline branch di conditional a edi kan no ho dwuma, a ɛyɛ papa. Afei foforo nso. Afei worekɔ superRefine sɛ encode cross-field mmara a wo Zod schema ntumi nkyerɛ wɔ ɔkwan a ɛyɛ daa so. Afei, anammɔn navigation fi ase leaking adwumayɛ ntease. Edu baabi a, wohwɛ nea woakyekye na wuhu sɛ kratasin no nyɛ UI ankasa bio. Ɛyɛ gyinaesi nhyehyɛe kɛse, na component dua no wɔ baabi a ɛbae sɛ wode siee no ara pɛ. Eyi ne baabi a misusuw sɛ adwene mu nhwɛsode a ɛfa nsusuwii ahorow ho wɔ React mu no sɛe, na nokwarem no ɛnyɛ obiara mfomso. RHF + Zod stack no ye yiye wɔ nea wɔyɛɛ no ​​maa no no mu. Asɛm no ne sɛ yɛtaa kɔ so de di dwuma boro baabi a ne abstractions ne ɔhaw no hyia efisɛ ɔkwan foforo no hwehwɛ sɛ yɛfa ɔkwan foforo so susuw nsusuwii ahorow ho koraa. Asɛm yi fa saa ɔkwan foforo no ho. Sɛ yɛbɛkyerɛ yei a, yɛbɛkyekyere multi-step form koro no ara pɛpɛɛpɛ mprenu:

Sɛ wode React Hook Form + Zod wired kɔ React Query so ma wɔmfa nkɔma a, . Ne SurveyJS, a ɛfa fom bi ho sɛ data — JSON schema a ɛnyɛ den — sen sɛ ɛbɛyɛ component dua.

Ahwehwɛde koro no ara, tebea mu ntease koro no ara, API frɛ koro no ara wɔ awiei. Afei yɛbɛma nea etu ne nea ɛtraa hɔ no ho mfonini pɛpɛɛpɛ, na yɛde ɔkwan a mfaso wɔ so a yɛbɛfa so asi nhwɛsode a ɛsɛ sɛ wode di dwuma ho gyinae, ne bere a ɛsɛ sɛ wode di dwuma no ato hɔ. Krataa a yɛrekyekye no:

Saa kratasin yi de anammɔn 4 a ɛsen bedi dwuma: Anamɔn 1: Nsɛm a ɛkɔ akyiri

Edin a edi kan (wɔhwehwɛ), . Email (ɛhwehwɛ, ɔkwan a ɛfata).

Anamɔn 2: Nhyehyɛe

Unit bo, . Dodow, . Towtua dodow, . Nea wonya fii mu: Nkyekyɛm nketewa, . Towtua, . Ne nyinaa.

Anamɔn 3: Akontaabu & Nsɛm a Wɔde Ma

So wowɔ akontaabu bi? (Yiw/Dabi) . Sɛ Yiw → username + password a, abien no nyinaa ho hia. Sɛ Dabi → email a wɔaboaboa ano dedaw wɔ anammɔn 1 mu a.

Abotɔyam a wɔde ma (1–5) . Sɛ ≥ 4 → bisa sɛ “Dɛn na w’ani gyee ho?” Sɛ ≤ 2 → bisa sɛ “Dɛn na yebetumi atu mpɔn?”

Anamɔn 4: Hwɛ mu bio

Sɛ ne nyinaa yɛ >= 100 nkutoo a, ɛbɛda adi Nsɛm a etwa to a wɔde bɛkɔ.

Eyi nyɛ nea ɛtra so. Nanso ɛdɔɔso sɛ ɛbɛda nsonsonoe a ɛwɔ adansi mu adi. Ɔfã 1: Nneɛma a Wɔde Di Dwuma (React Hook Form + Zod) . Installation a wɔde hyɛ mu npm instɔl react-hook-form zod @hookform/resolvers @tanstack/react-asɛmmisa

Zod Nhyehyɛe a Wɔde Yɛ Adwuma Momma yɛnhyɛ aseɛ wɔ Zod schema no so, ɛfiri sɛ ɛhɔ na ɛtaa yɛ baabi a form no nsusuiɛ no si hɔ. Wɔ anammɔn mmienu a ɛdi kan no — ankorankoro nsɛm ne ahyɛdeɛ a wɔde hyɛ mu — biribiara yɛ tẽẽ: nhama a wɔhwehwɛ, nɔma a ɛwɔ minimums, ne enum. Ɔfã a ɛyɛ anigye no fi ase bere a wobɔ mmɔden sɛ wobɛda mmara a ɛwɔ tebea mu no adi no.

fa { z } fi "zod" mu;

export const formSchema = z.object ({ edin a edi kan: z.string ().min (1, "Wɔhwehwɛ"), email: z.string ().email ("Imel a ɛnteɛ"), bo: z.nɔma (). min (0), dodow: z.nɔma (). min (1), towBo: z.nɔma (), hasAccount: z.enum (["Yiw", "Dabi"]), ɔdefo din: z.string ().optional (), password: z.string ().optional (), abotɔyam: z.number ().min (1).max (5), positiveFeedback: z.string ().optional (), nkɔsoFeedback: z.string ().optional (),}).superRefine ((data, ctx) => { sɛ (data.hasAccount === "Yes") { sɛ (!data.asɛmfua din) { ctx.addIssue({ koodu: "asɛmfua", ɔkwan: ["ɔdefo din"], nkrasɛm: "Wɔhwehwɛ" }); } if (!data.asɛmfua || data.asɛmfua.tenten < 6) { ctx.addIssue({ koodu: "asɛmfua", ɔkwan: ["asɛmfua"], nkrasɛm: "Min 6 nkyerɛwde" });

if (data.satisfaction >= 4 && !data.positiveFeedback) { ctx.addIssue({ code: "amammerɛ", kwan: ["positiveFeedback"], nkra: "Yɛsrɛ wo kyɛ nea w'ani gyee ho" }); } .

sɛ (data.abotɔyam <= 2 && !data.nkɔsoFeedback) { ctx.addIssue ({ koodu: "amammerɛ", ɔkwan:["improvementFeedback"], message: "Yɛsrɛ wo ka nea ɛsɛ sɛ yɛtu mpɔn kyerɛ yɛn" }); }});

export type FormData = z.infer <ɔkwan a wɔfa so yɛ adwuma>;

Hyɛ no nsow sɛ wɔakyerɛw username ne password sɛ optional() ɛwom mpo sɛ wɔhwehwɛ wɔ tebea mu efisɛ Zod’s type-level schema kyerɛkyerɛ ade no nsusuwii mu, ɛnyɛ mmara a ɛkyerɛ bere a afuw ho hia. Ɛsɛ sɛ tebea ahwehwɛde no tra superRefine mu, a ɛkɔ so bere a wɔagye nsusuwii no atom akyi na ɛwɔ kwan kɔ ade mũ no nyinaa so. Saa ntetewmu no nyɛ sintɔ; ɛyɛ nea wɔayɛ adwinnade no ama no ara kwa: superRefine ne baabi a cross-field logic kɔ bere a wontumi nkyerɛ wɔ schema nhyehyɛe no ankasa mu. Nea ɛda nsow nso wɔ ha ne nea saa nhyehyɛe yi nkyerɛ. Enni nkratafa ho adwene biara, enni mfuw a wotumi hu wɔ beae bɛn ho adwene biara, na enni adwene biara a ɛfa akwantu ho. Ɛno nyinaa bɛtra baabi foforo. Form Component a Wɔde Di Dwuma

import { useForm, useWatch } fi "react-hook-form";fa { zodResolver } fi "@hookform/resolvers/zod";fa { useMutation } fi "@tanstack/react-query";fa { useState, useMemo } fi "react";fa { formSchema, type FormData } fi "./schema";

const STEPS = ["nsɛm a ɛkɔ akyiri", "hyɛde", "akontaabu", "nhwehwɛmu"];

type OrderPayload = FormData & { ne nyinaa: nɔma; tow: nɔma; ne nyinaa: nɔma };

export function RHFMultiStepForm () { const [anammɔn, setAnamɔn] = fa Tebea di dwuma (0);

const mutation = fa di dwumaNsakrae ({ mutationFn: async (adesoa a mfaso wɔ so: OrderPayload) => { const res = twɛn fetch ("/ api/ahyɛde", { ɔkwan: "POST", . headers: { "Nsɛm a ɛwɔ mu-Type": "application/json" }, nipadua: JSON.stringify (adesoa a mfaso wɔ so), . }); if (!res.ok) throw new Error("Wɔantumi amfa ankɔma"); san kɔ res.json (); }, . });

const { register, control, handleSubmit, formState: { mfomso }, } = useForm ({ ano aduru: zodResolver (formSchema), defaultValues: { bo: 0, dodow: 1, towRate: 0.1, abotɔyam: 3, hasAccount: "Dabi", }, }); const bo = useWatch ({ tumidi, din: "bo" }); const dodow = useWatch ({ control, din: "dodow" }); const taxRate = useWatch ({ tumidi, din: "towBo" }); const wɔAkontaabu = useWatch ({ control, din: "wɔAkontaabu" }); const abotɔyam = useWatch ({ control, din: "abotɔyam" }); const subtotal = useMemo (() => (bo ?? 0) * (dodow ?? 1), [bo, dodow]); const tow = useMemo (() => subtotal * (tow dodow ?? 0), [tow dodow, towBo]); const total = useMemo (() => subtotal + tow, [nkyɛm ketewa, tow]); const onSubmit = (data: FormData) => mutation.mutate ({ ...data, dodow ketewa, tow, ne nyinaa }); const showSubmit = (anammɔn === 2 && ne nyinaa < 100) || (anammɔn === 3 && ne nyinaa >= 100)

return (

{anamɔn === 0 && ( <> )}

{anamɔn === 1 && ( <>

Nneɛma nketewa: {nkyɛm ketewa}
Tow: {tow}
Ne nyinaa: {ne nyinaa}
)}

{step === 2 && ( <>

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

{abotɔyam >= 4 && (