Ushbu maqola SurveyJS homiyligida Aksariyat React ishlab chiquvchilari uni hech qachon baland ovozda muhokama qilmasdan baham ko'radigan aqliy model mavjud. Bu shakllar har doim komponentlar bo'lishi kerak. Bu shunday stek degan ma'noni anglatadi:

Mahalliy shtat uchun React Hook shakli (minimal qayta ishlash, ergonomik maydonni ro'yxatdan o'tkazish, imperativ shovqin). Tekshirish uchun zod (kiritish to'g'riligi, chegara tekshiruvi, turdagi xavfsiz tahlil). Backend uchun javob so'rovi: yuborish, qayta urinishlar, keshlash, server bilan sinxronlash va hokazo.

Va ko'pgina shakllar uchun - kirish ekranlaringiz, sozlamalar sahifalaringiz, CRUD modallaringiz - bu juda yaxshi ishlaydi. Har bir qism o'z vazifasini bajaradi, ular toza tuzadilar va siz ilovangizning mahsulotingizni haqiqatda farq qiladigan qismlariga o'tishingiz mumkin. Ammo vaqti-vaqti bilan shakl oldingi javoblarga bog'liq ko'rinish qoidalari yoki uchta maydon bo'ylab kaskad bo'lgan olingan qiymatlar kabi narsalarni to'plashni boshlaydi. Balki o'tkazib yuborilishi yoki ishlayotgan jami asosida ko'rsatilishi kerak bo'lgan butun sahifalar ham bo'lishi mumkin. Birinchi shartni useWatch va inline filiali bilan boshqarasiz, bu yaxshi. Keyin boshqa. Keyin siz Zod sxemangiz odatdagi tarzda ifoda eta olmaydigan o'zaro faoliyat qoidalarini kodlash uchun superRefine-ga murojaat qilyapsiz. Keyin, qadam navigatsiya biznes mantig'ini oqizishni boshlaydi. Bir nuqtada siz o'zingiz yaratgan narsaga qaraysiz va shakl endi UI emasligini tushunasiz. Bu ko'proq qaror qabul qilish jarayoni va komponentlar daraxti aynan siz uni saqlagan joyingizdir. Menimcha, bu erda React-dagi shakllarning aqliy modeli buziladi va bu haqiqatan ham hech kimning aybi emas. RHF + Zod to'plami nima uchun mo'ljallanganligida juda yaxshi. Muammo shundaki, biz uni abstraktsiyalari muammoga to'g'ri keladigan nuqtadan keyin foydalanishni davom ettiramiz, chunki alternativa shakllar haqida butunlay boshqacha fikrlashni talab qiladi. Ushbu maqola ushbu alternativa haqida. Buni ko'rsatish uchun biz bir xil ko'p bosqichli shaklni ikki marta quramiz:

Taqdim etish uchun React Query-ga ulangan React Hook Form + Zod bilan, Shaklni komponentlar daraxti emas, balki oddiy JSON sxemasi sifatida ko'rib chiqadigan SurveyJS bilan.

Xuddi shu talablar, bir xil shartli mantiq, oxirida bir xil API chaqiruvi. Keyin biz ko'chirilgan va nima qolganligini aniq xaritaga kiritamiz va qaysi modeldan qachon va qachon foydalanish kerakligini hal qilishning amaliy usulini ishlab chiqamiz. Biz yaratayotgan shakl:

Bu shakl 4 bosqichli oqimdan foydalanadi: 1-qadam: Tafsilotlar

Ismingiz (majburiy), Elektron pochta (majburiy, yaroqli format).

2-qadam: Buyurtma

Birlik narxi, Miqdori, Soliq stavkasi, Olingan: oraliq jami, soliq, Jami.

3-qadam: Hisob va fikr-mulohaza

Hisobingiz bormi? (Ha/Yo'q) Ha → foydalanuvchi nomi + parol bo'lsa, ikkalasi ham talab qilinadi. Agar Yo'q bo'lsa → elektron pochta 1-bosqichda to'plangan.

Qoniqish darajasi (1–5) Agar ≥ 4 boʻlsa → “Sizga nima yoqdi?” deb soʻrang. Agar ≤ 2 boʻlsa → “Biz nimani yaxshilashimiz mumkin?” deb soʻrang.

4-qadam: Ko'rib chiqish

Faqat jami >= 100 bo'lsa paydo bo'ladi Yakuniy topshirish.

Bu ekstremal emas. Ammo bu me'moriy farqlarni ochish uchun etarli. 1-qism: Komponentga asoslangan (React Hook Form + Zod) O'rnatish npm o'rnatish react-hook-form zod @hookform/resolvers @tanstack/react-query

Zod sxemasi Keling, Zod sxemasidan boshlaylik, chunki bu erda odatda shakl shakli o'rnatiladi. Dastlabki ikki bosqichda - shaxsiy ma'lumotlar va buyurtma ma'lumotlari - hamma narsa oddiy: kerakli satrlar, minimal raqamlar va raqamlar. Qiziqarli qism shartli qoidalarni ifodalashga harakat qilganda boshlanadi.

"zod" dan { z } import qilish;

eksport const formSchema = z.object({ firstName: z.string().min(1, "Zarur"), email: z.string().email("Noto'g'ri email"), narx: z.number().min(0), miqdor: z.number().min(1), taxRate: z.number(), hasAccount(),"Yo'q username[])," z.string().ixtiyoriy(), parol: z.string().ixtiyoriy(), qoniqish: z.number().min(1).maks(5), pozitivFeedback: z.string().ixtiyoriy(), improvementFeedback: z.string().ixtiyoriy(),}).superRefine((maʼlumotlar, ctxda) ="Y {ma'lumotlar, ctxda ="A {agar}, agar (count) ="s. (!data.username) { ctx.addIssue({ kod: "custom", path: ["foydalanuvchi nomi"], xabar: "Talab qilinadi" }); }

agar (data.satisfaction >= 4 && !data.positiveFeedback) { ctx.addIssue({kod: "custom", path: ["positiveFeedback"], xabar: "Iltimos, sizga yoqqan narsani baham ko'ring" }); }

agar (data.satisfaction <= 2 && !data.improvementFeedback) { ctx.addIssue ({ kod: "custom", yo'l:["improvementFeedback"], xabar: "Iltimos, bizga nimani yaxshilash kerakligini ayting" }); }});

eksport turi FormData = z.infer;

E'tibor bering, foydalanuvchi nomi va parol shartli ravishda talab qilingan bo'lsa ham, ixtiyoriy() sifatida kiritiladi, chunki Zod turi darajasidagi sxemasi maydonlar muhim bo'lgan vaqtni tartibga soluvchi qoidalarni emas, balki ob'ekt shaklini tavsiflaydi. Shartli talab superRefine ichida bo'lishi kerak, u shakl tasdiqlangandan keyin ishlaydi va to'liq ob'ektga kirish huquqiga ega. Bu ajralish kamchilik emas; bu vosita aynan nima uchun mo'ljallangan: superRefine - bu sxema tuzilishining o'zida ifodalash mumkin bo'lmaganda o'zaro faoliyat mantiqqa o'tadigan joy. Bu erda ham diqqatga sazovor narsa, bu sxema nimani ifodalamaydi. Unda sahifalar tushunchasi, qaysi maydonlar qaysi nuqtada ko‘rinib turishi va navigatsiya tushunchasi yo‘q. Bularning barchasi boshqa joyda yashaydi. Shakl komponenti

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

const STEPS = ["tafsilotlar", "buyurtma", "hisob", "ko'rib chiqish"];

turi OrderPayload = FormData & {oraliq jami: raqam; soliq: raqam; jami: soni };

eksport funksiyasi RHFMultiStepForm() { const [qadam, setStep] = useState(0);

const mutatsiya = useMutation({ mutationFn: asinxron (foydali yuk: OrderPayload) => { const res = olishni kuting ("/api/orders", { usul: "POST", sarlavhalar: { "Content-Type": "application/json" }, Tana: JSON.stringify (foydali yuk), }); agar (!res.ok) yangi Xato ("Yuborish muvaffaqiyatsiz tugadi"); qaytish res.json(); }, });

const { register, control, handleSubmit, formState: { xatolar }, } = useForm({solver: zodResolver(formSchema), defaultValues: { narx: 0, miqdor: 1, taxRate: 0,1, qoniqish: 3, hasAccount: "Yo'q", },); const narx = useWatch ({ nazorat, nomi: "narx" }); const miqdori = useWatch ({ nazorat, nomi: "miqdor" }); const taxRate = useWatch ({ nazorat, nomi: "taxRate" }); const hasAccount = useWatch ({ boshqaruv, nomi: "hasAccount" }); const qoniqish = useWatch ({ nazorat, nomi: "qoniqish" }); const subjami = useMemo(() => (narxi ?? 0) * (miqdori ?? 1), [narx, miqdor]); const soliq = useMemo(() => subjami * (taxRate ?? 0), [subjami, taxRate]); const jami = useMemo(() => oraliq jami + soliq, [jami jami, soliq]); const onSubmit = (ma'lumotlar: FormData) => mutatsiya.mutatsiya ({ ...ma'lumotlar, oraliq jami, soliq, jami }); const showSubmit = (qadam === 2 && jami < 100) || (qadam === 3 && jami >= 100)

qaytish (

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

{qadam === 1 && ( <> 5%

Subjami: {subtotal}
Soliq: {tax}
Jami: {jami}
)}

{qadam === 2 && ( <>

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

{qoniqish >= 4 && (