Bu məqalə SurveyJS tərəfindən maliyyələşdirilir Əksər React tərtibatçılarının heç vaxt yüksək səslə müzakirə etmədən paylaşdıqları zehni model var. Bu formalar həmişə komponentlər olmalıdır. Bu, belə bir yığın deməkdir:

Yerli dövlət üçün React Hook Forması (minimal renderlər, erqonomik sahə qeydiyyatı, imperativ qarşılıqlı əlaqə). Doğrulama üçün zod (girişin düzgünlüyü, sərhədin yoxlanılması, tip üçün təhlükəsiz təhlil). Backend üçün React Query: təqdim etmə, təkrar cəhdlər, keşləmə, server sinxronizasiyası və s.

Və formaların böyük əksəriyyəti üçün - giriş ekranlarınız, parametrlər səhifələriniz, CRUD modallarınız - bu, həqiqətən yaxşı işləyir. Hər bir parça öz işini görür, onlar təmiz tərtib edirlər və siz tətbiqinizin məhsulunuzu həqiqətən fərqləndirən hissələrinə keçə bilərsiniz. Amma hərdən bir forma, əvvəlki cavablardan asılı olan görünmə qaydaları və ya üç sahədən keçən əldə edilmiş dəyərlər kimi şeylər toplamağa başlayır. Ola bilsin ki, hətta qaçırılmalı və ya çalışan cəminə əsasən göstərilməli olan bütün səhifələr. Birinci şərti useWatch və daxili budaqla idarə edirsiniz, bu yaxşıdır. Sonra başqa. Sonra Zod sxeminizin normal şəkildə ifadə edə bilmədiyi çarpaz sahə qaydalarını kodlaşdırmaq üçün superRefine-ə müraciət edirsiniz. Sonra addım naviqasiyası biznes məntiqini sızdırmağa başlayır. Bir nöqtədə, siz qurduğunuza baxırsınız və formanın artıq həqiqətən UI olmadığını başa düşürsünüz. Bu, daha çox qərar vermə prosesidir və komponent ağacı məhz onu saxladığınız yerdir. Düşünürəm ki, React-də formalar üçün zehni model pozulur və bu, həqiqətən heç kimin günahı deyil. RHF + Zod yığını nə üçün nəzərdə tutulduğuna görə əladır. Məsələ ondadır ki, biz ondan abstraksiyaların problemə uyğun gəldiyi nöqtədən sonra istifadə etməyə davam edirik, çünki alternativ formalar haqqında tamamilə fərqli düşüncə tərzini tələb edir. Bu məqalə həmin alternativ haqqındadır. Bunu göstərmək üçün eyni çoxaddımlı formanı iki dəfə quracağıq:

Təqdim etmək üçün React Query-ə qoşulmuş React Hook Form + Zod ilə, Formanı komponent ağacı kimi deyil, sadə JSON sxemi kimi qəbul edən SurveyJS ilə.

Eyni tələblər, eyni şərti məntiq, sonunda eyni API çağırışı. Sonra nəyin köçürüldüyünü və nəyin qaldığını dəqiq şəkildə xəritələşdirəcəyik və hansı modeli və nə vaxt istifadə edəcəyinizə qərar vermək üçün praktiki yol təqdim edəcəyik. Qurduğumuz forma:

Bu formada 4 addımlı axın istifadə olunacaq: Addım 1: Təfərrüatlar

Ad (tələb olunur), E-poçt (tələb olunur, etibarlı format).

Addım 2: Sifariş verin

Vahid qiyməti, Kəmiyyət, Vergi dərəcəsi, Alınan: Ara cəmi, vergi, Cəmi.

Addım 3: Hesab və Rəy

Sizin hesabınız var? (Bəli/Xeyr) Əgər Bəli → istifadəçi adı + parol, hər ikisi tələb olunur. Əgər Xeyr → e-poçt artıq 1-ci addımda toplanıb.

Məmnuniyyət reytinqi (1-5) Əgər ≥ 4 olarsa → “Nəyi bəyəndiniz?” sualını verin. Əgər ≤ 2 olarsa → “Nəyi təkmilləşdirə bilərik?” sualını verin.

Addım 4: Nəzərdən keçirin

Yalnız cəmi >= 100 olduqda görünür Yekun təqdimat.

Bu ifrat deyil. Ancaq memarlıq fərqlərini üzə çıxarmaq üçün kifayətdir. 1-ci hissə: Komponentlə idarə olunur (Reaksiya Qarmaq Forması + Zod) Quraşdırma npm quraşdırın react-hook-form zod @hookform/resolvers @tanstack/react-query

Zod sxemi Zod sxemindən başlayaq, çünki formanın forması adətən burada qurulur. İlk iki addım üçün - şəxsi təfərrüatlar və sifariş daxiletmələri - hər şey sadədir: tələb olunan sətirlər, minimumları olan nömrələr və nömrə. Maraqlı hissə şərti qaydaları ifadə etməyə çalışdığınız zaman başlayır.

"zod"dan { z } idxal edin;

ixrac const formSchema = z.object({ firstName: z.string().min(1, "Tələb olunur"), e-poçt: z.string().email("Yanlış e-poçt"), qiymət: z.number().min(0), miqdar: z.number().min(1), taxRate: z.number(), hasAccount()(), hasAccount()"z:" z.string().optional(), parol: z.string().optional(), məmnunluq: z.number().min(1).max(5), müsbət Geribildirim: z.string().optional(), improvementFeedback: z.string().optional(),}).superRefine((məlumat, ctxda) ="sAc(əgər data, ctxda=esta. (!data.username) { ctx.addIssue({ kod: "xüsusi", yol: ["istifadəçi adı"], mesaj: "Tələb olunur" }); } if (!data.password || data.password.length < 6) { ctx.addIssue({ kod: "xüsusi ", söz "M"}}; }

əgər (data.satisfaction >= 4 && !data.positiveFeedback) { ctx.addIssue({ kod: "xüsusi", yol: ["pozitiv rəy"], mesaj: "Bəyəndiyinizi paylaşın" }); }

əgər (data.satisfaction <= 2 && !data.improvementFeedback) { ctx.addIssue({ kod: "xüsusi", yol:["improvementFeedback"], mesaj: "Lütfən, bizə nəyi təkmilləşdirəcəyimizi deyin" }); }});

ixrac növü FormData = z.infer;

Nəzərə alın ki, istifadəçi adı və parol şərti olaraq tələb olunsa da, isteğe bağlı() kimi yazılır, çünki Zodun tip səviyyəli sxemi sahələrin vacib olduğu zamanı tənzimləyən qaydaları deyil, obyektin formasını təsvir edir. Şərti tələb, forma təsdiq edildikdən sonra işləyən və tam obyektə çıxışı olan superRefine daxilində yaşamalıdır. Bu ayrılıq qüsur deyil; alət məhz bunun üçün nəzərdə tutulub: superRefine, diaqram strukturunun özündə ifadə edilə bilməyən sahələr arası məntiqin getdiyi yerdir. Burada diqqət çəkən məqam bu sxemin ifadə etmədiyi şeylərdir. Onun nə səhifələr anlayışı, nə hansı sahənin hansı nöqtədə göründüyü, nə də naviqasiya anlayışı var. Bütün bunlar başqa yerdə yaşayacaq. Forma Komponenti

import { useForm, useWatch } "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 = ["ətraflı", "sifariş", "hesab", "nəzarət"];

type OrderPayload = FormData & { subtotal: number; vergi: nömrə; cəmi: sayı };

ixrac funksiyası RHFMultiStepForm() { const [addım, setStep] = useState(0);

const mutasiya = useMutation({ mutationFn: async (faydalı yük: OrderPayload) => { const res = alınmasını gözləyin("/api/orders", { üsul: "POST", başlıqlar: { "Content-Type": "application/json" }, bədən: JSON.stringify (faydalı yük), }); əgər (!res.ok) yeni Xəta atsa ("Göndərmək mümkün olmadı"); res.json(); }, });

const { qeydiyyatdan keçin, nəzarət edin, handleSubmit, formState: { xətalar }, } = useForm({ həlledici: zodResolver(formSchema), defaultValues: { qiymət: 0, miqdar: 1, taxRate: 0.1, məmnunluq: 3, hasAccount: "Xeyr", },}); const qiymət = useWatch({ nəzarət, ad: "qiymət" }); const kəmiyyət = useWatch({ nəzarət, ad: "miqdar" }); const taxRate = useWatch({ nəzarət, ad: "taxRate" }); const hasAccount = useWatch({ nəzarət, ad: "hasAccount" }); const məmnuniyyət = useWatch({ nəzarət, ad: "məmnuniyyət" }); const subtotal = useMemo(() => (qiymət ?? 0) * (kəmiyyət ?? 1), [qiymət, miqdar]); const vergi = useMemo(() => subcəm * (taxRate ?? 0), [alt cəmi, taxRate]); const total = useMemo(() => subcəm + vergi, [alt cəmi, vergi]); const onSubmit = (məlumat: FormData) => mutasiya.mutasiya ({ ...məlumat, ara cəmi, vergi, cəmi }); const showSubmit = (addım === 2 && cəmi < 100) || (addım === 3 && cəmi >= 100)

qaytarın (

{addım === 0 && ( <> )}

{addım === 1 && ( <>

Alt cəmi: {subtotal}
Vergi: {tax}
Cəmi: {cəmi}
)}

{addım === 2 && ( <>

{hasAccount === "Bəli" && ( <> )}

{məmnunluq >= 4 && (