এই নিবন্ধটি SurveyJS দ্বারা স্পনসর করা হয়েছে এমন একটি মানসিক মডেল রয়েছে যা বেশিরভাগ প্রতিক্রিয়া বিকাশকারীরা কখনও জোরে আলোচনা না করেই শেয়ার করে। যে ফর্ম সবসময় উপাদান হতে অনুমিত হয়. এর মানে একটি স্ট্যাক যেমন:

স্থানীয় রাজ্যের জন্য প্রতিক্রিয়া হুক ফর্ম (ন্যূনতম রি-রেন্ডার, ergonomic ক্ষেত্রের নিবন্ধন, অপরিহার্য মিথস্ক্রিয়া)। বৈধকরণের জন্য Zod (ইনপুট সঠিকতা, সীমানা যাচাইকরণ, টাইপ-নিরাপদ পার্সিং)। ব্যাকএন্ডের জন্য প্রতিক্রিয়া ক্যোয়ারী: জমা, পুনরায় চেষ্টা, ক্যাশিং, সার্ভার সিঙ্ক এবং আরও অনেক কিছু।

এবং বেশিরভাগ ফর্মের জন্য - আপনার লগইন স্ক্রীন, আপনার সেটিংস পৃষ্ঠা, আপনার CRUD মডেলগুলি - এটি সত্যিই ভাল কাজ করে৷ প্রতিটি অংশ তার কাজ করে, তারা পরিষ্কারভাবে রচনা করে এবং আপনি আপনার অ্যাপ্লিকেশনের অংশগুলিতে যেতে পারেন যা আসলে আপনার পণ্যকে আলাদা করে। কিন্তু প্রতি একবার কিছুক্ষণের মধ্যে, একটি ফর্ম দৃশ্যমানতার নিয়মগুলির মতো জিনিসগুলি জমা করতে শুরু করে যা পূর্ববর্তী উত্তরগুলির উপর নির্ভর করে, বা প্রাপ্ত মানগুলি যা তিনটি ক্ষেত্রের মধ্য দিয়ে ক্যাসকেড করে। হতে পারে এমনকি পুরো পৃষ্ঠাগুলি যা বাদ দেওয়া উচিত বা চলমান মোটের উপর ভিত্তি করে দেখানো উচিত। আপনি একটি useWatch এবং একটি ইনলাইন শাখার সাথে প্রথম শর্তসাপেক্ষে পরিচালনা করেন, যা ঠিক আছে। তারপর আরেকটা। তারপরে আপনি ক্রস-ফিল্ড নিয়মগুলিকে এনকোড করতে সুপাররিফাইনের কাছে পৌঁছেছেন যা আপনার Zod স্কিমা স্বাভাবিক উপায়ে প্রকাশ করতে পারে না। তারপরে, ধাপে ন্যাভিগেশন ব্যবসার যুক্তি ফাঁস শুরু করে। কিছু সময়ে, আপনি যা তৈরি করেছেন তা দেখেন এবং বুঝতে পারেন যে ফর্মটি আসলে আর UI নয়। এটি একটি সিদ্ধান্ত প্রক্রিয়ার বেশি, এবং উপাদান গাছটি ঠিক যেখানে আপনি এটি সংরক্ষণ করেছেন। এখানেই আমি মনে করি প্রতিক্রিয়ার ফর্মগুলির জন্য মানসিক মডেল ভেঙে যায় এবং এটি সত্যিই কারও দোষ নয়। আরএইচএফ + জোড স্ট্যাকটি যার জন্য ডিজাইন করা হয়েছিল তার জন্য দুর্দান্ত। সমস্যাটি হল যে আমরা এটিকে বিন্দুর আগে ব্যবহার করতে থাকি যেখানে এর বিমূর্ততাগুলি সমস্যার সাথে মেলে কারণ বিকল্পটির সম্পূর্ণরূপে ফর্ম সম্পর্কে চিন্তা করার একটি ভিন্ন উপায় প্রয়োজন। এই নিবন্ধটি সেই বিকল্প সম্পর্কে। এটি দেখানোর জন্য, আমরা একই মাল্টি-স্টেপ ফর্মটি দুবার তৈরি করব:

রিঅ্যাক্ট হুক ফর্ম + জোড জমা দেওয়ার জন্য রিঅ্যাক্ট কোয়েরির সাথে সংযুক্ত, 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 স্কিমা দিয়ে শুরু করা যাক, কারণ এটি সাধারণত যেখানে ফর্মের আকৃতি প্রতিষ্ঠিত হয়। প্রথম দুই ধাপের জন্য — ব্যক্তিগত বিবরণ এবং অর্ডার ইনপুট — সবকিছুই সোজা: প্রয়োজনীয় স্ট্রিং, ন্যূনতম সংখ্যা এবং একটি enum। আপনি শর্তসাপেক্ষ নিয়ম প্রকাশ করার চেষ্টা করার সময় আকর্ষণীয় অংশ শুরু হয়।

"zod" থেকে { z } আমদানি করুন;

রপ্তানি কনস্ট ফর্মস্কেমা = z.অবজেক্ট({ firstName: z.string().min(1, "আবশ্যক"), ইমেল: z.string().email("অবৈধ ইমেল"), মূল্য: z.number().min(0), পরিমাণ: z.number().min(1), ট্যাক্স রেট: z.number().min(1), ট্যাক্স রেট: z.number(", [Yumeunt(), আছে "No"]), ব্যবহারকারীর নাম: z.string().optional(), password: z.string().optional(), satisfaction: z.number().min(1).max(5), positive Feedback: z.string().optional(), improvementfeedback: z.string().optional(ef), ine (fine, }) super == (data.hasAccount === "হ্যাঁ") { if (!data.username) { ctx.addIssue({ কোড: "কাস্টম", পথ: ["ব্যবহারকারীর নাম"], বার্তা: "প্রয়োজনীয়" }); ["পাসওয়ার্ড"], বার্তা: "সর্বনিম্ন ৬ অক্ষর" } });

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" টাইপ করুন, "ডিমা" টাইপ করুন।

const STEPS = ["বিস্তারিত", "অর্ডার", "অ্যাকাউন্ট", "রিভিউ"];

টাইপ করুন OrderPayload = FormData & { সাবটোটাল: সংখ্যা; ট্যাক্স: সংখ্যা; মোট: সংখ্যা };

এক্সপোর্ট ফাংশন RHFMultiStepForm() { const [step, setStep] = useState(0);

const mutation = useMutation({ mutationFn: async (payload: OrderPayload) => { const res = আনার অপেক্ষা করুন("/api/orders", { পদ্ধতি: "পোস্ট", শিরোনাম: { "Content-Type": "application/json" }, বডি: JSON.stringify(পেলোড), }); যদি (!res.ok) নতুন ত্রুটি নিক্ষেপ ("সাবমিট করতে ব্যর্থ"); res.json(); }, });

const { register, control, handleSubmit, formState: { errors }, } = useForm({ solver: 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(() => সাবটোটাল * (ট্যাক্সরেট?? 0), [সাবটোটাল, ট্যাক্সরেট]); const total = useMemo(() => সাবটোটাল + ট্যাক্স, [সাবটোটাল, ট্যাক্স]); const onSubmit = (data: FormData) => mutation.mutate({ ...data, subtotal, tax, total }); const showSubmit = (ধাপ === 2 && মোট < 100) || (ধাপ === 3 && মোট >= 100)

ফেরত দিন (

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

{step === 1 && ( <>

সাবটোটাল: {subtotal}
কর: {tax}
মোট: {total}
)}

{পদক্ষেপ === 2 && ( <>

{hasAccount === "হ্যাঁ" && ( <> <ইনপুট {...register("username")} placeholder="Username" /> )}

{সন্তুষ্টি >= 4 && (