हा लेख SurveyJS द्वारे प्रायोजित आहे एक मानसिक मॉडेल आहे जे बहुतेक प्रतिक्रिया विकसक कधीही मोठ्याने चर्चा न करता सामायिक करतात. ते फॉर्म नेहमी घटक असावेत. याचा अर्थ असा स्टॅक आहे:

स्थानिक राज्यासाठी प्रतिक्रिया हुक फॉर्म (किमान री-रेंडर, एर्गोनॉमिक फील्ड नोंदणी, अनिवार्य परस्परसंवाद). प्रमाणीकरणासाठी झोड (इनपुट अचूकता, सीमा प्रमाणीकरण, टाइप-सेफ पार्सिंग). बॅकएंडसाठी प्रतिक्रिया क्वेरी: सबमिशन, पुन्हा प्रयत्न, कॅशिंग, सर्व्हर सिंक इ.

आणि बहुसंख्य फॉर्मसाठी — तुमची लॉगिन स्क्रीन, तुमची सेटिंग्ज पेज, तुमचे CRUD मॉडेल — हे खरोखर चांगले काम करते. प्रत्येक तुकडा त्याचे कार्य करतो, ते स्वच्छपणे तयार करतात आणि आपण आपल्या अनुप्रयोगाच्या भागांवर जाऊ शकता जे आपल्या उत्पादनामध्ये फरक करतात. परंतु प्रत्येक वेळी, एक फॉर्म पूर्वीच्या उत्तरांवर अवलंबून असलेल्या दृश्यमानतेचे नियम किंवा तीन फील्डमधून कॅस्केड होणारी व्युत्पन्न मूल्ये यासारख्या गोष्टी जमा करू लागतो. कदाचित संपूर्ण पृष्ठे जी वगळली जावीत किंवा चालू असलेल्या एकूण संख्येवर आधारित दर्शविली जावीत. तुम्ही युजवॉच आणि इनलाइन शाखेसह पहिले सशर्त हाताळता, जे ठीक आहे. मग दुसरा. मग तुम्ही क्रॉस-फील्ड नियम एन्कोड करण्यासाठी सुपररिफाइनपर्यंत पोहोचत आहात जे तुमचा Zod स्कीमा सामान्य पद्धतीने व्यक्त करू शकत नाही. नंतर, स्टेप नेव्हिगेशन व्यवसाय तर्क गळती सुरू होते. काही क्षणी, तुम्ही काय तयार केले आहे ते तुम्ही पाहता आणि लक्षात येते की फॉर्म आता खरोखर UI नाही. ही एक निर्णय प्रक्रिया आहे, आणि घटक वृक्ष आहे जिथे तुम्ही ते संग्रहित केले आहे. इथेच मला वाटते की प्रतिक्रिया मधील फॉर्म्सचे मानसिक मॉडेल खंडित होते आणि यात खरोखर कोणाचाही दोष नाही. RHF + Zod स्टॅक ज्यासाठी डिझाइन केले होते त्यामध्ये उत्कृष्ट आहे. समस्या अशी आहे की आम्ही ते वापरत राहण्याचा कल असतो जिथे त्याचे अमूर्त समस्येशी जुळतात कारण पर्यायाला पूर्णपणे फॉर्म्सबद्दल विचार करण्याच्या वेगळ्या पद्धतीची आवश्यकता असते. हा लेख त्या पर्यायाबद्दल आहे. हे दर्शविण्यासाठी, आम्ही तंतोतंत समान मल्टी-स्टेप फॉर्म दोनदा तयार करू:

React Hook Form + Zod सह React Query सबमिट करण्यासाठी वायर्ड, SurveyJS सह, जे फॉर्मला डेटा म्हणून हाताळते — एक साधा JSON स्कीमा — घटक वृक्षाऐवजी.

समान आवश्यकता, समान सशर्त तर्क, शेवटी समान API कॉल. मग आम्ही नेमके काय हलवले आणि काय राहिले याचा नकाशा बनवू आणि तुम्ही कोणते मॉडेल आणि कधी वापरायचे हे ठरवण्याचा एक व्यावहारिक मार्ग तयार करू. आम्ही तयार करत असलेला फॉर्म:

हा फॉर्म 4-चरण प्रवाह वापरेल: पायरी 1: तपशील

नाव (आवश्यक), ईमेल (आवश्यक, वैध स्वरूप).

पायरी 2: ऑर्डर करा

युनिट किंमत, प्रमाण, कर दर, व्युत्पन्न: उपएकूण, कर, एकूण.

पायरी 3: खाते आणि फीडबॅक

तुमचे खाते आहे का? (होय/नाही) होय असल्यास → वापरकर्तानाव + पासवर्ड, दोन्ही आवश्यक. जर नाही → ईमेल आधीच चरण 1 मध्ये संकलित केले आहे.

समाधान मानांकन (1-5) जर ≥ 4 → "तुम्हाला काय आवडले?" विचारले तर जर ≤ 2 → विचारले की "आम्ही काय सुधारू शकतो?"

पायरी 4: पुनरावलोकन करा

एकूण >= १०० असल्यासच दिसते अंतिम सबमिशन.

हे टोकाचे नाही. परंतु आर्किटेक्चरल फरक उघड करण्यासाठी ते पुरेसे आहे. भाग 1: घटक-चालित (प्रतिक्रिया हुक फॉर्म + झोड) स्थापना npm install react-hook-form 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), कर दर: z.number().min(1), करदर: z.cumber(", [Yumeunt(), आहे. "नाही"]), वापरकर्तानाव: z.string().optional(), पासवर्ड: z.string().optional(), satisfaction: z.number().min(1).max(5), positiveFeedback: z.string().optional(), improvementFeedback: z.string().optional(ef), ine(ct(fine), }(dai) = super. (data.hasAccount === "होय") { if (!data.username) { ctx.addIssue({ code: "custom", path: ["username"], message: "Required" }); } if (!data.password || data.password.length x. cuss{Ict: 6) ["पासवर्ड"], संदेश: "किमान 6 वर्ण" } });

जर (data.satisfaction >= 4 && !data.positiveFeedback) { ctx.addIssue({ code: "custom", path: ["positiveFeedback"], संदेश: "कृपया तुम्हाला काय आवडले ते शेअर करा" }); }

जर (data.satisfaction <= 2 && !data.improvementFeedback) { ctx.addIssue({ code: "custom", path:["improvementFeedback"], संदेश: "कृपया आम्हाला काय सुधारायचे ते सांगा" }); }});

निर्यात प्रकार FormData = z.infer;

लक्षात घ्या की वापरकर्तानाव आणि संकेतशब्द सशर्त आवश्यक असला तरीही पर्यायी() म्हणून टाईप केले आहेत कारण Zod चा प्रकार-स्तरीय स्कीमा ऑब्जेक्टच्या आकाराचे वर्णन करते, फील्ड महत्त्वाच्या असताना नियमन करणारे नियम नाही. सशर्त आवश्यकता सुपररिफाइनमध्ये राहणे आवश्यक आहे, जे आकार प्रमाणित झाल्यानंतर चालते आणि पूर्ण ऑब्जेक्टमध्ये प्रवेश आहे. तो वियोग हा दोष नाही; हे साधन कशासाठी डिझाइन केले आहे ते आहे: सुपररिफाईन हे जेथे क्रॉस-फील्ड लॉजिक जाते जेव्हा ते स्कीमा स्ट्रक्चरमध्ये व्यक्त केले जाऊ शकत नाही. हे स्कीमा व्यक्त करत नाही ते येथे देखील उल्लेखनीय आहे. यात पृष्ठांची संकल्पना नाही, कोणत्या फील्ड्स कोणत्या बिंदूवर दृश्यमान आहेत याची कल्पना नाही आणि नेव्हिगेशनची संकल्पना नाही. ते सर्व कुठेतरी राहतील. फॉर्म घटक

"react-hook-form" वरून { useForm, useWatch } आयात करा; "@hookform/resolvers/zod" वरून { zodResolver } आयात करा; "@tanstack/react-query" वरून { useMutation } आयात करा; "react" वरून { useState, useMemo } आयात करा; "react" वरून { useState, useMemo } आयात करा; "maata" फॉर्म वरून आयात करा;

const STEPS = ["तपशील", "ऑर्डर", "खाते", "पुनरावलोकन"];

टाइप करा OrderPayload = FormData & { उपटोटल: संख्या; कर: संख्या; एकूण: संख्या };

निर्यात कार्य RHFMultiStepForm() { const [step, setStep] = useState(0);

const mutation = useMutation({ mutationFn: async (payload: OrderPayload) => { const res = आणण्याची प्रतीक्षा करा("/api/orders", { पद्धत: "पोस्ट", शीर्षलेख: { "सामग्री-प्रकार": "अनुप्रयोग/json" }, मुख्य भाग: JSON.stringify(पेलोड), }); जर (!res.ok) नवीन त्रुटी टाकली ("सबमिट करण्यात अयशस्वी"); res.json(); }, });

const { register, control, handleSubmit, formState: { errors }, } = useForm({ निराकरणकर्ता: zodResolver(formSchema), defaultValues: { price: 0, quantity: 1, tax Rate: 0.1, satisfaction: 3,}}; const price = useWatch({ control, name: "price" }); const quantity = useWatch({ control, name: "quantity" }); const taxRate = useWatch({ control, name: "taxRate" }); const hasAccount = useWatch({ control, name: "hasAccount" }); const satisfaction = useWatch({ control, name: "satisfaction" }); 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({ ...data, subtotal, tax, total }); const showSubmit = (चरण === 2 && एकूण < 100) || (चरण === 3 && एकूण >= 100)

परत करा (

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

{चरण === 1 && ( <> <निवडा {...register("taxRate", 5%

Subtotal: {subtotal}
कर: {tax}
एकूण: {total}
)}

{चरण === 2 && ( <>

{hasAccount === "होय" && ( <> <इनपुट {...register("username")} placeholder="Username" /> )}

{समाधान >= ४ && (