یہ مضمون سروے جے ایس کے زیر اہتمام ہے۔ ایک ذہنی ماڈل ہے جو زیادہ تر React ڈویلپرز اس پر کبھی بھی اونچی آواز میں بات کیے بغیر شیئر کرتے ہیں۔ اس فارم کو ہمیشہ اجزاء سمجھا جاتا ہے۔ اس کا مطلب ہے اسٹیک جیسے:

مقامی ریاست کے لیے ری ایکٹ ہک فارم (کم سے کم ری رینڈرز، ایرگونومک فیلڈ رجسٹریشن، لازمی تعامل)۔ توثیق کے لیے زوڈ (ان پٹ درستگی، باؤنڈری کی توثیق، ٹائپ سیف پارسنگ)۔ بیک اینڈ کے لیے ردعمل کا سوال: جمع کرانے، دوبارہ کوششیں، کیشنگ، سرور کی مطابقت پذیری، اور اسی طرح کی۔

اور فارموں کی اکثریت کے لیے — آپ کی لاگ ان اسکرینز، آپ کی ترتیبات کے صفحات، آپ کے CRUD ماڈلز — یہ واقعی اچھی طرح کام کرتا ہے۔ ہر ٹکڑا اپنا کام کرتا ہے، وہ صاف ستھرا کمپوز کرتے ہیں، اور آپ اپنی ایپلی کیشن کے ان حصوں کی طرف جا سکتے ہیں جو آپ کی پروڈکٹ کو حقیقت میں مختلف کرتے ہیں۔ لیکن ہر ایک بار تھوڑی دیر میں، ایک فارم چیزوں کو جمع کرنا شروع کر دیتا ہے جیسے مرئیت کے اصول جو پہلے کے جوابات پر منحصر ہوتے ہیں، یا اخذ کردہ اقدار جو تین شعبوں سے گزرتی ہیں۔ ہو سکتا ہے کہ پورے صفحات کو چھوڑ دیا جائے یا چلنے والے کل کی بنیاد پر دکھایا جائے۔ آپ یوز واچ اور ان لائن برانچ کے ساتھ پہلی مشروط کو ہینڈل کرتے ہیں، جو کہ ٹھیک ہے۔ پھر ایک اور۔ پھر آپ کراس فیلڈ رولز کو انکوڈ کرنے کے لیے سپر ریفائن تک پہنچ رہے ہیں جن کا آپ کا Zod اسکیما عام انداز میں اظہار نہیں کر سکتا۔ پھر، مرحلہ وار نیویگیشن کاروباری منطق کو لیک کرنا شروع کر دیتی ہے۔ کسی وقت، آپ دیکھتے ہیں کہ آپ نے کیا بنایا ہے اور احساس ہوتا ہے کہ فارم واقعی UI نہیں ہے۔ یہ فیصلہ کرنے کا زیادہ عمل ہے، اور جزو کا درخت وہیں ہے جہاں آپ نے اسے ذخیرہ کیا تھا۔ یہ وہ جگہ ہے جہاں میرے خیال میں React میں شکلوں کا ذہنی ماڈل ٹوٹ جاتا ہے، اور یہ واقعی کسی کی غلطی نہیں ہے۔ RHF + Zod اسٹیک اس کے لیے بہترین ہے جس کے لیے اسے ڈیزائن کیا گیا تھا۔ مسئلہ یہ ہے کہ ہم اسے اس نقطہ سے گزرتے رہتے ہیں جہاں اس کے تجریدات مسئلے سے میل کھاتے ہیں کیونکہ متبادل کو مکمل طور پر شکلوں کے بارے میں سوچنے کا ایک مختلف طریقہ درکار ہوتا ہے۔ یہ مضمون اس متبادل کے بارے میں ہے۔ اس کو دکھانے کے لیے، ہم دو بار بالکل وہی ملٹی سٹیپ فارم بنائیں گے:

React Hook Form + Zod کے ساتھ React Query جمع کرانے کے لیے، SurveyJS کے ساتھ، جو ایک فارم کو ڈیٹا کے طور پر لیتا ہے — ایک سادہ JSON اسکیما — بجائے کہ ایک جزو درخت۔

وہی تقاضے، وہی مشروط منطق، آخر میں وہی API کال۔ پھر ہم بالکل نقشہ بنائیں گے کہ کیا منتقل ہوا اور کیا ٹھہرا، اور یہ فیصلہ کرنے کے لیے ایک عملی طریقہ ترتیب دیں گے کہ آپ کو کون سا ماڈل استعمال کرنا چاہیے، اور کب۔ فارم جو ہم بنا رہے ہیں:

یہ فارم 4 قدمی بہاؤ کا استعمال کرے گا: مرحلہ 1: تفصیلات

پہلا نام (ضروری) ای میل (ضروری، درست فارمیٹ)۔

مرحلہ 2: آرڈر کریں۔

یونٹ کی قیمت، مقدار، ٹیکس کی شرح، ماخوذ: ذیلی کل، ٹیکس، کل

مرحلہ 3: اکاؤنٹ اور تاثرات

کیا آپ کا اکاؤنٹ ہے؟ (ہاں/نہیں) اگر ہاں → صارف نام + پاس ورڈ، دونوں کی ضرورت ہے۔ اگر نہیں → ای میل پہلے ہی مرحلہ 1 میں جمع ہو چکی ہے۔

اطمینان کی درجہ بندی (1–5) اگر ≥ 4 → پوچھے "آپ کو کیا پسند ہے؟" اگر ≤ 2 → پوچھے "ہم کیا بہتر کر سکتے ہیں؟"

مرحلہ 4: جائزہ لیں۔

صرف تب ظاہر ہوتا ہے جب کل >= 100 ہو۔ حتمی جمع کروانا۔

یہ انتہا نہیں ہے۔ لیکن یہ تعمیراتی اختلافات کو بے نقاب کرنے کے لئے کافی ہے۔ حصہ 1: اجزاء سے چلنے والا (ری ایکٹ ہک فارم + زوڈ) تنصیب npm install react-hook-form zod @hookform/resolvers @tanstack/react-query

زود سکیما آئیے Zod اسکیما کے ساتھ شروع کریں، کیونکہ عام طور پر یہ وہ جگہ ہے جہاں فارم کی شکل قائم ہوتی ہے۔ پہلے دو مراحل کے لیے — ذاتی تفصیلات اور آرڈر کی معلومات — سب کچھ سیدھا ہے: مطلوبہ تار، کم از کم نمبر، اور ایک اینوم۔ دلچسپ حصہ اس وقت شروع ہوتا ہے جب آپ مشروط قواعد کو بیان کرنے کی کوشش کرتے ہیں۔

"zod" سے { z } درآمد کریں؛

ایکسپورٹ const formSchema = z.object({ firstName: z.string().min(1, "Required"), email: z.string().email("غلط ای میل"), قیمت: z.number().min(0), مقدار: z.number().min(1), tax Rate: z.number().min(1), tax Rate: z.number(", . "No"])، صارف کا نام: z.string().optional(), پاس ورڈ: z.string().optional(), satisfaction: z.number().min(1).max(5), positive Feedback: z.string().optional(), improvement Feedback: z.string().optional(ef), ine (fine) =(xta))super (data.hasAccount === "Yes") { if (!data.username) { ctx.addIssue({ code: "custom", path: ["username"], message: "Required" } }; ["پاس ورڈ"]، پیغام: "کم سے کم 6 حروف" })؛

if (data.satisfaction >= 4 && !data.positiveFeedback) { ctx.addIssue({ code: "custom", path: ["positiveFeedback"], پیغام: "براہ کرم شیئر کریں جو آپ کو پسند آیا" }); }

اگر (data.satisfaction <= 2 && !data.improvementFeedback) { ctx.addIssue({ کوڈ: "کسٹم"، راستہ:["improvementFeedback"], پیغام: "براہ کرم ہمیں بتائیں کہ کیا بہتر کرنا ہے" }); }});

برآمد کی قسم FormData = z.infer؛

نوٹ کریں کہ صارف نام اور پاس ورڈ بطور اختیاری () ٹائپ کیے گئے ہیں حالانکہ وہ مشروط طور پر درکار ہیں کیونکہ Zod کا ٹائپ لیول سکیما آبجیکٹ کی شکل کو بیان کرتا ہے، نہ کہ اصولوں کی جب فیلڈز کی اہمیت ہو۔ مشروط ضرورت کو سپر ریفائن کے اندر رہنا پڑتا ہے، جو شکل کی توثیق کے بعد چلتا ہے اور اسے مکمل آبجیکٹ تک رسائی حاصل ہوتی ہے۔ وہ جدائی کوئی عیب نہیں ہے۔ یہ صرف وہی ہے جس کے لیے ٹول ڈیزائن کیا گیا ہے: سپر ریفائن وہ جگہ ہے جہاں کراس فیلڈ منطق اس وقت جاتی ہے جب اسکیما ڈھانچے میں ہی اس کا اظہار نہیں کیا جاسکتا ہے۔ یہاں جو چیز بھی قابل ذکر ہے وہ یہ ہے کہ اس اسکیما کا اظہار نہیں کیا گیا ہے۔ اس میں صفحات کا کوئی تصور نہیں ہے، اس کا کوئی تصور نہیں ہے کہ کون سے فیلڈز کس مقام پر نظر آتے ہیں، اور نہ ہی نیویگیشن کا کوئی تصور۔ یہ سب کہیں اور رہیں گے۔ فارم کا جزو

"react-hook-form" سے { useForm, useWatch } درآمد کریں؛ "@hookform/resolvers/zod" سے { zodResolver } درآمد کریں؛ "@tanstack/react-query" سے { useMutation } درآمد کریں؛ "react" سے { useState, useMemo } درآمد کریں؛ "react" سے درآمد کریں؛ "maata" فارم سے درآمد کریں؛ "/Dma" فارم سے درآمد کریں۔

const STEPS = ["تفصیلات"، "آرڈر"، "اکاؤنٹ"، "جائزہ"]؛

قسم OrderPayload = FormData & { ذیلی ٹوٹل: نمبر؛ ٹیکس: نمبر؛ کل: نمبر }؛

ایکسپورٹ فنکشن RHFMultiStepForm() { const [step, setStep] = useState(0)؛

const mutation = useMutation({ mutationFn: async (payload: OrderPayload) => { const res = انتظار کریں بازیافت ("/api/orders"، { طریقہ: "پوسٹ"، ہیڈر: { "مواد کی قسم": "درخواست/json" }، باڈی: JSON.stringify(payload)، }); اگر (!res.ok) نئی خرابی پھینک دیں("جمع کرانے میں ناکام")؛ واپسی res.json(); }، });

const {رجسٹر، کنٹرول، ہینڈل جمع کروائیں، فارم اسٹیٹ: { errors }، } = useForm({ حل کرنے والا: zodResolver(formSchema))، ڈیفالٹ ویلیوز: { قیمت: 0، مقدار: 1، ٹیکس کی شرح: 0.1، اطمینان: 3، }؛ اطمینان: 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: "stisfaction" }); const subtotal = useMemo(() => (قیمت ?? 0) * (مقدار ?? 1)، [قیمت، مقدار])؛ const tax = useMemo(() => ذیلی ٹوٹل * (ٹیکس کی شرح ?? 0)، [ذیلی ٹوٹل، ٹیکس کی شرح]؛ const total = useMemo(() => subtotal + tax, [subtotal, tax]); const onSubmit = (data: FormData) => mutation.mutate({ ...data, subtotal, tax, total }); const showSubmit = (مرحلہ === 2 && کل <100) || (مرحلہ === 3 اور کل >= 100)

واپس کریں (

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

{step === 1 && ( <>

Subtotal: {subtotal}
Tax: {tax}
کل: {total}
)}

{step === 2 && ( <>

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

{اطمینان >= 4 اور& (