এই লেখাটো SurveyJS ৰ পৃষ্ঠপোষকতাত লিখা হৈছে বেছিভাগ React ডেভেলপাৰে কেতিয়াও ডাঙৰকৈ আলোচনা নকৰাকৈ শ্বেয়াৰ কৰা এটা মানসিক মডেল আছে৷ যে ৰূপবোৰ সদায় উপাদান হোৱাৰ কথা। ইয়াৰ অৰ্থ হৈছে এটা ষ্টেক যেনে:

স্থানীয় অৱস্থাৰ বাবে ৰিএক্ট হুক ফৰ্ম (নূন্যতম পুনৰ ৰেণ্ডাৰ, এৰগ'নমিক ক্ষেত্ৰ পঞ্জীয়ন, আৱশ্যকীয় পাৰস্পৰিক ক্ৰিয়া)। বৈধকৰণৰ বাবে Zod (ইনপুট শুদ্ধতা, সীমা বৈধকৰণ, ধৰণ-নিৰাপদ বিশ্লেষণ)। বেকএণ্ডৰ বাবে React Query: দাখিল, পুনৰ চেষ্টা, কেচিং, চাৰ্ভাৰ ছিঙ্ক, ইত্যাদি।

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

React Hook Form + Zod জমা দিয়াৰ বাবে React Query লৈ তাঁৰযুক্ত, SurveyJS ৰ সৈতে, যি এটা প্ৰপত্ৰক তথ্য হিচাপে গণ্য কৰে — এটা সৰল JSON আঁচনি — এটা উপাদান বৃক্ষৰ পৰিবৰ্তে।

একে প্ৰয়োজনীয়তা, একে চৰ্তযুক্ত যুক্তি, শেষত একে এপিআই কল। তাৰ পিছত আমি কি স্থানান্তৰিত হ’ল আৰু কি থাকিল সেইটো সঠিকভাৱে মেপ কৰিম, আৰু আপুনি কোনটো মডেল ব্যৱহাৰ কৰিব লাগে, আৰু কেতিয়া ব্যৱহাৰ কৰিব লাগে সেইটো নিৰ্ণয় কৰাৰ এটা ব্যৱহাৰিক উপায় ৰাখিম৷ আমি নিৰ্মাণ কৰা ফৰ্মখন:

এই প্ৰপত্ৰত ৪-পদক্ষেপৰ প্ৰবাহ ব্যৱহাৰ কৰা হ'ব: স্তৰ ১: সবিশেষ

প্ৰথম নাম (প্ৰয়োজনীয়), ইমেইল (প্ৰয়োজনীয়, বৈধ বিন্যাস)।

২য় পদক্ষেপ: অৰ্ডাৰ কৰক

ইউনিটৰ মূল্য, পৰিমাণ, কৰ হাৰ, ব্যুৎপত্তি: উপমুঠ, কৰ, মুঠ।

স্তৰ ৩: একাউণ্ট আৰু মতামত

আপোনাৰ একাউণ্ট আছে নেকি? (হয়/নাই) যদি হয় → ব্যৱহাৰকাৰীৰ নাম + গুপ্তশব্দ, দুয়োটাৰ প্ৰয়োজন। যদি নাই → ইতিমধ্যে স্তৰ 1 ত সংগ্ৰহ কৰা ইমেইল।

সন্তুষ্টিৰ ৰেটিং (1–5) যদি ≥ 4 → সুধিব “আপুনি কি ভাল পাইছিল?” যদি ≤ 2 → সুধিব “আমি কি উন্নত কৰিব পাৰো?”

৪ৰ্থ স্তৰ: পুনৰীক্ষণ কৰক

কেৱল যদি মুঠ >= 100 হয় তেন্তেহে দেখা দিয়ে চূড়ান্ত জমা দিয়া।

এইটো চৰম নহয়। কিন্তু স্থাপত্যৰ পাৰ্থক্য উন্মোচন কৰিবলৈ ই যথেষ্ট৷ অংশ ১: উপাদান-চালিত (প্ৰতিক্ৰিয়া হুক ফৰ্ম + Zod) সংস্থাপন npm react-hook-form zod @hookform/resolvers @tanstack/react-query সংস্থাপন কৰক

জোড আঁচনি আৰম্ভ কৰোঁ Zod আঁচনিৰ পৰা, কাৰণ সাধাৰণতে তাতেই ফৰ্মৰ আকৃতি প্ৰতিষ্ঠা হয়। প্ৰথম দুটা পদক্ষেপৰ বাবে — ব্যক্তিগত বিৱৰণ আৰু অৰ্ডাৰ ইনপুটসমূহ — সকলো পোনপটীয়া: প্ৰয়োজনীয় ষ্ট্ৰিংসমূহ, নূন্যতমসমূহৰ সৈতে সংখ্যাসমূহ, আৰু এটা enum। চৰ্তযুক্ত নিয়মবোৰ প্ৰকাশ কৰিবলৈ চেষ্টা কৰিলে আকৰ্ষণীয় অংশটো আৰম্ভ হয়।

"zod" ৰ পৰা { z } আমদানি কৰক;

export const formSchema = z.object({ firstName: z.string().min(1, "প্ৰয়োজনীয়"), ইমেইল: z.string().email("অবৈধ ইমেইল"), মূল্য: z.number().min(0), পৰিমাণ: z.number().min(1), taxRate: z.number(), hasAccount: z.enum(["হয়", "নাই"]), ব্যৱহাৰকাৰীৰ নাম: z.string().ঐচ্ছিক(), পাছৱৰ্ড: z.string().ঐচ্ছিক(), সন্তুষ্টি: z.number().min(1).max(5), positiveFeedback: z.string().ঐচ্ছিক(), উন্নতিৰFeedback: z.string().ঐচ্ছিক(),}).superRefine((ডাটা, ctx) => { যদি (data.hasAccount === "হয়") { যদি (!data.username) { ctx.addIssue({ ক'ড: "স্বনিৰ্বাচিত", পথ: ["ব্যৱহাৰকাৰীৰ নাম"], বাৰ্তা: "প্ৰয়োজনীয়" });

if (data.satisfaction >= 4 && !data.positiveFeedback) { ctx.addIssue({ ক'ড: "স্বনিৰ্বাচিত", পথ: ["ধনাত্মক মতামত"], বাৰ্তা: "আপুনি যি ভাল পাইছে সেয়া অনুগ্ৰহ কৰি শ্বেয়াৰ কৰক" }); }

যদি (তথ্য.সন্তুষ্টি <= 2 && !ডাটা.উন্নতিৰ মতামত) { ctx.addIssue({ ক'ড: "স্বনিৰ্বাচিত", পথ:["improvementFeedback"], message: "অনুগ্ৰহ কৰি আমাক কি উন্নত কৰিব লাগে কওক" }); }});

ৰপ্তানিৰ ধৰণ FormData = z.infer;

মন কৰক যে ব্যৱহাৰকাৰীৰ নাম আৰু পাছৱৰ্ডক ঐচ্ছিক() হিচাপে টাইপ কৰা হয় যদিও সিহঁত চৰ্তসাপেক্ষভাৱে প্ৰয়োজনীয় কাৰণ Zod ৰ টাইপ-স্তৰৰ আঁচনিয়ে বস্তুটোৰ আকৃতি বৰ্ণনা কৰে, ক্ষেত্ৰসমূহে কেতিয়া গুৰুত্বপূৰ্ণ হয় সেইটো নিয়ন্ত্ৰণ কৰা নিয়মসমূহ নহয়। চৰ্তযুক্ত প্ৰয়োজনীয়তা superRefine ৰ ভিতৰত থাকিব লাগিব, যি আকৃতি বৈধ হোৱাৰ পিছত চলে আৰু সম্পূৰ্ণ বস্তুটোৰ অভিগম আছে। সেই বিচ্ছেদ কোনো ত্ৰুটি নহয়; ইয়াৰ বাবে সঁজুলিটো ডিজাইন কৰা হৈছে: superRefine হৈছে য’ত ক্ৰছ-ফিল্ড লজিক যায় যেতিয়া ইয়াক আঁচনিৰ গঠনত প্ৰকাশ কৰিব নোৱাৰি। ইয়াত যিটো কথাও উল্লেখযোগ্য সেয়া হ’ল এই আঁচনিখনে কি প্ৰকাশ নকৰে। ইয়াত পৃষ্ঠাৰ কোনো ধাৰণা নাই, কোনটো ক্ষেত্ৰ কোনটো বিন্দুত দেখা যায় তাৰ কোনো ধাৰণা নাই আৰু নেভিগেচনৰ কোনো ধাৰণা নাই। সেই সকলোবোৰ আন ঠাইত থাকিব। ফৰ্ম উপাদান

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

const STEPS = ["বিৱৰণ", "ক্ৰম", "একাউন্ট", "পৰ্যালোচনা"];

type OrderPayload = FormData & { উপমুঠ: সংখ্যা; কৰ: সংখ্যা; মুঠ: সংখ্যা };

ৰপ্তানি ফাংচন RHFMultiStepForm() { const [পদক্ষেপ, setStep] = useState(0);

const মিউটেচন = ব্যৱহাৰ মিউটেচন({ mutationFn: async (পেলোড: অৰ্ডাৰপেলোড) => { const res = অপেক্ষা কৰক fetch("/api/orders", { পদ্ধতি: "POST", headers: { "সামগ্ৰী-প্ৰকাৰ": "এপ্লিকেচন/json" }, শৰীৰ: JSON.stringify(পেলোড), }); if (!res.ok) throw new Error("জমা দিয়াত ব্যৰ্থ"); res.json() ঘূৰাই দিয়ক; }, });

const { পঞ্জীয়ন, নিয়ন্ত্ৰণ, handleSubmit, formState: { ভুল }, } = useForm({ সমাধানকাৰী: zodResolver(formSchema), অবিকল্পিতমূল্য: { মূল্য: 0, পৰিমাণ: 1, taxRate: 0.1, সন্তুষ্টি: 3, hasAccount: "নাই", }, }); const price = useWatch({ নিয়ন্ত্ৰণ, নাম: "মূল্য" }); const পৰিমাণ = useWatch({ নিয়ন্ত্ৰণ, নাম: "পৰিমাণ" }); const taxRate = useWatch({ নিয়ন্ত্ৰণ, নাম: "কৰৰ হাৰ" }); const hasAccount = useWatch({ নিয়ন্ত্ৰণ, নাম: "একাউন্ট আছে" }); const satisfaction = useWatch({ নিয়ন্ত্ৰণ, নাম: "সন্তুষ্টি" }); const subtotal = useMemo(() => (মূল্য ?? 0) * (পৰিমাণ ?? 1), [মূল্য, পৰিমাণ]); const tax = useMemo(() => উপমুঠ * (কৰৰ হাৰ ?? 0), [উপমুঠ, কৰৰ হাৰ]); const মুঠ = useMemo (() => উপমুঠ + কৰ, [উপমুঠ, কৰ]); const onSubmit = (তথ্য: FormData) => mutation.mutate({ ...তথ্য, উপমুঠ, কৰ, মুঠ }); const showSubmit = (পদক্ষেপ === ২ && মুঠ < ১০০) || (পদক্ষেপ === ৩ && মুঠ >= ১০০)

return (

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

{step === 1 && ( <> <ইনপুট ধৰণ="সংখ্যা" {...register("মূল্য", { valueAsNumber: সত্য })} /> <নিৰ্বাচন কৰক {...register("taxRate", { valueAsNumber: true })}>

উপমুঠ: {উপমুঠ}
কৰ: {tax}
মুঠ: {মুঠ}
)}

{step === 2 && ( <>

{hasAccount === "হয়" && ( <> )}

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