ఈ కథనం SurveyJS ద్వారా స్పాన్సర్ చేయబడింది చాలా మంది రియాక్ట్ డెవలపర్‌లు బిగ్గరగా చర్చించకుండా భాగస్వామ్యం చేసే మానసిక నమూనా ఉంది. ఆ రూపాలు ఎల్లప్పుడూ భాగాలుగా భావించబడతాయి. దీని అర్థం ఇలాంటి స్టాక్:

స్థానిక స్థితి కోసం రియాక్ట్ హుక్ ఫారమ్ (కనీస రీ-రెండర్‌లు, ఎర్గోనామిక్ ఫీల్డ్ రిజిస్ట్రేషన్, అత్యవసర పరస్పర చర్య). ధ్రువీకరణ కోసం Zod (ఇన్‌పుట్ ఖచ్చితత్వం, సరిహద్దు ధ్రువీకరణ, టైప్-సేఫ్ పార్సింగ్). బ్యాకెండ్ కోసం రియాక్ట్ క్వెరీ: సమర్పణ, పునఃప్రయత్నాలు, కాషింగ్, సర్వర్ సమకాలీకరణ మరియు మొదలైనవి.

మరియు మెజారిటీ ఫారమ్‌ల కోసం — మీ లాగిన్ స్క్రీన్‌లు, మీ సెట్టింగ్‌ల పేజీలు, మీ CRUD మోడల్స్ — ఇది నిజంగా బాగా పని చేస్తుంది. ప్రతి భాగం దాని పనిని చేస్తుంది, అవి శుభ్రంగా కంపోజ్ చేస్తాయి మరియు మీరు మీ ఉత్పత్తిని వేరు చేసే మీ అప్లికేషన్ యొక్క భాగాలకు వెళ్లవచ్చు. కానీ ప్రతిసారీ, ఒక ఫారమ్ మునుపటి సమాధానాలపై ఆధారపడిన విజిబిలిటీ నియమాలు లేదా మూడు ఫీల్డ్‌ల ద్వారా క్యాస్కేడ్ చేసే ఉత్పన్న విలువలు వంటి వాటిని సేకరించడం ప్రారంభిస్తుంది. స్కిప్ చేయబడే లేదా నడుస్తున్న మొత్తం ఆధారంగా చూపబడే మొత్తం పేజీలు కూడా ఉండవచ్చు. మీరు యూజ్‌వాచ్ మరియు ఇన్‌లైన్ బ్రాంచ్‌తో మొదటి షరతును నిర్వహిస్తారు, ఇది మంచిది. తర్వాత మరొకటి. మీ Zod స్కీమా సాధారణ పద్ధతిలో వ్యక్తీకరించలేని క్రాస్-ఫీల్డ్ నియమాలను ఎన్‌కోడ్ చేయడానికి మీరు సూపర్‌రిఫైన్‌ను చేరుకుంటున్నారు. అప్పుడు, స్టెప్ నావిగేషన్ వ్యాపార లాజిక్‌ను లీక్ చేయడం ప్రారంభిస్తుంది. ఏదో ఒక సమయంలో, మీరు ఏమి నిర్మించారో చూసి, ఆ ఫారమ్ నిజంగా UI కాదని తెలుసుకుంటారు. ఇది మరింత నిర్ణయాత్మక ప్రక్రియ, మరియు కాంపోనెంట్ ట్రీ మీరు దానిని నిల్వ చేసిన చోటే ఉంటుంది. ఇక్కడే రియాక్ట్‌లోని ఫారమ్‌ల మానసిక నమూనా విచ్ఛిన్నమైందని నేను భావిస్తున్నాను మరియు ఇది నిజంగా ఎవరి తప్పు కాదు. RHF + Zod స్టాక్ డిజైన్ చేయబడిన దానిలో అద్భుతమైనది. సమస్య ఏమిటంటే, దాని సంగ్రహణలు సమస్యతో సరిపోలే బిందువును దాటి మనం దాన్ని ఉపయోగిస్తూనే ఉంటాము ఎందుకంటే ప్రత్యామ్నాయానికి పూర్తిగా రూపాల గురించి వేరే ఆలోచనా విధానం అవసరం. ఈ వ్యాసం ఆ ప్రత్యామ్నాయం గురించి. దీన్ని చూపించడానికి, మేము ఒకే బహుళ-దశల ఫారమ్‌ను రెండుసార్లు నిర్మిస్తాము:

సమర్పణ కోసం రియాక్ట్ క్వెరీకి రియాక్ట్ హుక్ ఫారమ్ + Zod వైర్డుతో, సర్వేజేఎస్‌తో, ఇది ఫారమ్‌ను డేటాగా పరిగణిస్తుంది - సాధారణ JSON స్కీమా - కాంపోనెంట్ ట్రీగా కాకుండా.

అవే అవసరాలు, అదే షరతులతో కూడిన తర్కం, చివరిలో అదే API కాల్. అప్పుడు మేము ఏది తరలించబడిందో మరియు ఏది నిలిచిందో ఖచ్చితంగా మ్యాప్ చేస్తాము మరియు మీరు ఏ మోడల్‌ని మరియు ఎప్పుడు ఉపయోగించాలో నిర్ణయించడానికి ఒక ఆచరణాత్మక మార్గాన్ని రూపొందిస్తాము. మేము నిర్మిస్తున్న రూపం:

ఈ ఫారమ్ 4-దశల ప్రవాహాన్ని ఉపయోగిస్తుంది: దశ 1: వివరాలు

మొదటి పేరు (అవసరం), ఇమెయిల్ (అవసరం, చెల్లుబాటు అయ్యే ఫార్మాట్).

దశ 2: ఆర్డర్

యూనిట్ ధర, పరిమాణం, పన్ను రేటు, ఉద్భవించింది: ఉపమొత్తం, పన్ను, మొత్తం.

దశ 3: ఖాతా & అభిప్రాయం

మీకు ఖాతా ఉందా? (అవును/కాదు) అవును అయితే → వినియోగదారు పేరు + పాస్‌వర్డ్, రెండూ అవసరం. లేకపోతే → ఇమెయిల్ ఇప్పటికే దశ 1లో సేకరించబడింది.

సంతృప్తి రేటింగ్ (1–5) ఒకవేళ ≥ 4 → “మీకు ఏది నచ్చింది?” అని అడిగితే ఒకవేళ ≤ 2 → “మేము ఏమి మెరుగుపరచగలము?” అని అడిగితే

దశ 4: సమీక్షించండి

మొత్తం >= 100 అయితే మాత్రమే కనిపిస్తుంది తుది సమర్పణ.

ఇది విపరీతమైనది కాదు. కానీ వాస్తు వ్యత్యాసాలను బహిర్గతం చేయడానికి ఇది సరిపోతుంది. పార్ట్ 1: కాంపోనెంట్-డ్రైవెన్ (రియాక్ట్ హుక్ ఫారమ్ + జోడ్) సంస్థాపన npm ఇన్‌స్టాల్ రియాక్ట్-హుక్-ఫారమ్ zod @hookform/resolvers @tanstack/react-query

జోడ్ స్కీమా Zod స్కీమాతో ప్రారంభిద్దాం, ఎందుకంటే సాధారణంగా ఇక్కడ ఫారమ్ యొక్క ఆకృతి ఏర్పడుతుంది. మొదటి రెండు దశల కోసం - వ్యక్తిగత వివరాలు మరియు ఆర్డర్ ఇన్‌పుట్‌లు - ప్రతిదీ సూటిగా ఉంటుంది: అవసరమైన స్ట్రింగ్‌లు, కనిష్ట సంఖ్యలతో కూడిన సంఖ్యలు మరియు ఎన్యూమ్. మీరు షరతులతో కూడిన నియమాలను వ్యక్తీకరించడానికి ప్రయత్నించినప్పుడు ఆసక్తికరమైన భాగం ప్రారంభమవుతుంది.

"zod" నుండి {z } దిగుమతి;

ఎగుమతి const formSchema = z.object({ firstName: z.string().min(1, "అవసరం"), ఇమెయిల్: z.string().email("చెల్లని ఇమెయిల్"), ధర: z.number().min(0), పరిమాణం: z.number().min(1), taxRate: z.ncumberunt z.enum(["అవును", "లేదు"]), వినియోగదారు పేరు: z.string().ఐచ్ఛిక(), పాస్‌వర్డ్: z.string().ఐచ్ఛిక(), సంతృప్తి: z.number().min(1).max(5), పాజిటివ్ అభిప్రాయం: z.string().supptional(), మెరుగుదల ctx) => { if (data.hasAccount === "Yes") { if (!data.username) { ctx.addIssue({code: "custom", path: ["username"], message: "Required" }); {!data.password. 6d. "కస్టమ్", మార్గం: ["పాస్‌వర్డ్"], సందేశం: "కనిష్ట 6 అక్షరాలు" });

అయితే (data.satisfaction >= 4 && !data.positiveFeedback) { ctx.addIssue({ కోడ్: "కస్టమ్", మార్గం: ["పాజిటివ్ ఫీడ్‌బ్యాక్"], సందేశం: "దయచేసి మీకు నచ్చిన దాన్ని భాగస్వామ్యం చేయండి" }); }

ఉంటే (data.satisfaction <= 2 && !data.improvementFeedback) { ctx.addIssue({ కోడ్: "కస్టమ్", మార్గం:["మెరుగుదల అభిప్రాయం"], సందేశం: "దయచేసి ఏమి మెరుగుపరచాలో మాకు చెప్పండి" }); }});

ఎగుమతి రకం FormData = z.infer;

Zod యొక్క టైప్-లెవల్ స్కీమా ఆబ్జెక్ట్ ఆకారాన్ని వివరిస్తుంది, ఫీల్డ్‌లు ముఖ్యమైనప్పుడు నియమాలు కాకుండా, షరతులతో అవసరం అయినప్పటికీ వినియోగదారు పేరు మరియు పాస్‌వర్డ్ ఐచ్ఛికం()గా టైప్ చేయబడిందని గమనించండి. షరతులతో కూడిన ఆవశ్యకత సూపర్‌రిఫైన్‌లో నివసించాలి, ఇది ఆకారం ధృవీకరించబడిన తర్వాత నడుస్తుంది మరియు పూర్తి వస్తువుకు ప్రాప్యతను కలిగి ఉంటుంది. ఆ విభజన లోపం కాదు; ఇది సాధనం కోసం రూపొందించబడినది: సూపర్ రిఫైన్ అనేది స్కీమా నిర్మాణంలో వ్యక్తీకరించబడనప్పుడు క్రాస్-ఫీల్డ్ లాజిక్ ఎక్కడికి వెళుతుంది. ఈ స్కీమా వ్యక్తం చేయనిది కూడా ఇక్కడ గుర్తించదగినది. దీనికి పేజీల కాన్సెప్ట్ లేదు, ఏ సమయంలో ఏ ఫీల్డ్‌లు కనిపిస్తాయి అనే భావన లేదు మరియు నావిగేషన్ కాన్సెప్ట్ లేదు. అవన్నీ వేరే చోట నివసిస్తాయి. ఫారమ్ కాంపోనెంట్

"react-hook-form" నుండి {useForm, useWatch}ని దిగుమతి చేయండి;"@hookform/resolvers/zod" నుండి {zodResolver }ని దిగుమతి చేయండి;

const STEPS = ["వివరాలు", "ఆర్డర్", "ఖాతా", "సమీక్ష"];

టైప్ ఆర్డర్ పేలోడ్ = ఫారమ్‌డేటా & {ఉపమొత్తం: సంఖ్య; పన్ను: సంఖ్య; మొత్తం: సంఖ్య};

ఎగుమతి ఫంక్షన్ RHFMultiStepForm() {const [step, setStep] = useState(0);

const mutation = useMutation({ mutationFn: async (పేలోడ్: OrderPayload) => { const res = పొందేందుకు వేచి ఉండండి("/api/orders", { పద్ధతి: "పోస్ట్", శీర్షికలు: { "కంటెంట్-రకం": "application/json" }, శరీరం: JSON.stringify(పేలోడ్), }); ఒకవేళ (!res.ok) కొత్త లోపాన్ని ("సమర్పించడంలో విఫలమైంది"); res.json(); }, });

const {రిజిస్టర్, కంట్రోల్, హ్యాండిల్‌సబ్మిట్, ఫారమ్‌స్టేట్: {లోపాలు }, } = యూజ్‌ఫార్మ్({పరిష్కార: zodResolver(formSchema), డిఫాల్ట్ విలువలు: {ధర: 0, పరిమాణం: 1, పన్ను రేటు: 0.1, సంతృప్తి: 3, "No", }}); const price = useWatch({నియంత్రణ, పేరు: "ధర"}); const quantity = useWatch({నియంత్రణ, పేరు: "పరిమాణం"}); const taxRate = useWatch({నియంత్రణ, పేరు: "taxRate"}); const hasAccount = useWatch({నియంత్రణ, పేరు: "hasAccount"}); const satisfaction = useWatch({నియంత్రణ, పేరు: "సంతృప్తి"}); const subtotal = useMemo(() => (ధర ?? 0) * (పరిమాణం ?? 1), [ధర, పరిమాణం]); const tax = useMemo(() => subtotal * (taxRate ?? 0), [subtotal, taxRate]); const total = useMemo(() => subtotal + tax, [subtotal, tax]); const onSubmit = (డేటా: FormData) => mutation.mutate({ ...డేటా, ఉపమొత్తం, పన్ను, మొత్తం }); const showSubmit = (స్టెప్ === 2 && మొత్తం < 100) || (దశ === 3 && మొత్తం >= 100)

తిరిగి (

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

{step === 1 && ( <> <<< value="0.05">5%

ఉపమొత్తం: {subtotal}
పన్ను: {tax}
మొత్తం: {total}
)}

{step === 2 && ( <>

{hasAccount === "అవును" && ( <> )}

{సంతృప్తి >= 4 && (