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
qaytarın (
);}Altıncı sönmə ilə Pen SurveyJS-03-RHF [forked] baxın. Burada çox şey baş verir və işlərin harada bitdiyini görmək üçün yavaşlamağa dəyər.
Alınan dəyərlər - subtotal, vergi, total - komponentdə useWatch və useMemo vasitəsilə hesablanır, çünki onlar canlı sahə dəyərlərindən asılıdır və onlar üçün başqa təbii yer yoxdur. İstifadəçi adı, parol, müsbət Geribildirim və təkmilləşdirmə üçün görünmə qaydaları JSX-də daxili şərtlər kimi yaşayır. Addım atlama məntiqi — yalnız cəmi >= 100 olduqda görünən nəzərdən keçirmə səhifəsi — showSubmit dəyişəninə və 3-cü addımda göstərmə şərtinə daxil edilmişdir. Naviqasiyanın özü sadəcə əl ilə artırdığımız useState sayğacıdır. React Query təkrar cəhdləri, keşləməni və etibarsızlığı idarə edir. Forma yalnız təsdiqlənmiş məlumatlarla mutation.mutate çağırır.
Bunların heç biri özlüyündə səhv deyil. Bu, hələ də idiomatik Reaksiyadır və komponent RHF-nin yenidən renderləri necə təcrid etdiyinə görə olduqca performanslıdır. Ancaq bunu yazmamış birinə təhvil versəniz və onlardan nəzərdən keçirmə səhifəsinin hansı şəraitdə göründüyünü izah etmələrini istəsəniz, onlar bir sətirdə ifadə oluna bilən qaydanı yenidən qurmaq üçün showSubmit, 3-cü addım göstərmə şərti və naviqasiya düyməsinin məntiqini - üç ayrı yerdə izləməli olacaqlar. Forma işləyir, bəli, lakin davranış bir sistem olaraq həqiqətən yoxlanıla bilməz. Bunu zehni olaraq həyata keçirmək lazımdır. Daha da əhəmiyyətlisi, onu dəyişdirmək mühəndislik cəlbini tələb edir. Baxış addımının göründüyü zaman tənzimləmə kimi kiçik bir çimdik belə komponenti redaktə etmək, təsdiqləməni yeniləmək, çəkmə sorğusunu açmaq, nəzərdən keçirilməsini gözləmək və yenidən yerləşdirmək deməkdir. Hissə 2: Sxemaya əsaslanan (SurveyJS) İndi bir sxemdən istifadə edərək eyni axını quraq. Quraşdırma npm anket-core survey-react-ui @tanstack/react-query quraşdırın
Survey-core SurveyJS-in forma renderini gücləndirən MİT-lisenziyalı platformadan müstəqil iş vaxtı mühərriki – burada bizim diqqətimizi çəkən hissə. O, JSON sxemini götürür, ondan daxili model qurur və əks halda Reaksiya komponentinizdə yaşaya biləcək hər şeyi idarə edir: görünmə ifadələrini qiymətləndirmək, əldə edilmiş dəyərləri hesablamaq, səhifə vəziyyətini idarə etmək, yoxlamanı izləmək və hansı səhifələrin həqiqətən göstərildiyi nəzərə alınmaqla “tamamlama”nın nə demək olduğuna qərar vermək.
survey-react-uiHəmin modeli React ilə birləşdirən UI / göstərmə təbəqəsi. Bu, mahiyyət etibarı ilə mühərrikin vəziyyəti dəyişdikdə yenidən təqdim edilən
Birlikdə, onlar sizə tək bir nəzarət axını yazmadan tam funksional, çox səhifəli forma iş vaxtı verir. Sxem formatının özü, əvvəllər deyildiyi kimi, sadəcə JSON-dur - DSL və ya mülkiyyət hüququ yoxdur. Siz onu daxil edə, fayldan idxal edə, API-dən götürə və ya verilənlər bazası sütununda saxlaya və iş vaxtı nəmləndirə bilərsiniz. Data kimi eyni forma Budur, bu dəfə JSON obyekti kimi ifadə edilən eyni forma. Sxem hər şeyi müəyyən edir: struktur, doğrulama, görünmə qaydaları, əldə edilmiş hesablamalar, səhifə naviqasiyası — və onu iş vaxtında qiymətləndirən Modelə təhvil verir. Bunun tam olaraq göründüyü budur:
ixrac const surveySchema = { başlıq: "Sifariş axını", showProgressBar: "yuxarı", səhifələr: [ { ad: "təfsilatlar", elementlər: [ { növü: "mətn", ad: "firstName", tələb olunur: doğru }, { yazın: "mətn", ad: "e-poçt", inputType: "e-poçt", tələb olunur: "doğrudur: mətn ", [{ email" }] } ] }, { ad: "sifariş", elementlər: [ { növü: "mətn", ad: "qiymət", inputType: "nömrə", defaultValue: 0 }, { növü: "mətn", ad: "kəmiyyət", inputType: "nömrə", defaultValue: 1 }, { növü: "açılan",ad: "taxRate", defaultValue: 0.1, seçimlər: [ { dəyər: 0.05, mətn: "5%" }, { dəyər: 0.1, mətn: "10%" }, { dəyər: 0.15, mətn: "15%" } ] }, { növü: "ifadə", ad: "{antiqiyməti:quce", ad: "{antiqiyməti:quce", "ifadə", ad: "vergi", ifadə: "{subtotal} {taxRate}" }, { növü: "ifadə", ad: "cəmi", ifadə: "{alt cəmi} + {vergi}" } ] }, { ad: "hesab", elementlər: [ { növü: "radioqrup", ad: "hasAccount", ["] "növü: "t", seçimlər ad: "username", visibleIf: "{hasAccount} = 'Bəli'", isRequired: true }, { type: "text", name: "password", inputType: "password", visibleIf: "{hasAccount} = 'Bəli'", isTələb olunur: true, validators: [{hasAccount} = 'Bəli': [{hasAccount}, min. simvol" }] }, { növü: "reytinq", ad: "məmnuniyyət", dərəcəsiMin: 1, dərəcəsiMaks: 5 }, { növü: "şərh", ad: "pozitiv rəy", visibleIf: "{məmnuniyyət} >= 4" }, { yazın: "şərh", ad: "improvementFeed": "{satisfaction:" <} ] }, { ad: "incələmə", visibleIf: "{total} >= 100", elementlər: [] } ]};
Bunu bir anlıq RHF versiyası ilə müqayisə edin.
Şərti olaraq tələb olunan istifadəçi adı və şifrə olan superRefine bloku getdi. visibleIf: "{hasAccount} = 'Bəli'" ilə birlikdə isRequired: true hər iki narahatlığı birlikdə, onları tapmağı gözlədiyiniz sahənin özündə idarə edir. Aralıq cəmi, vergi və cəmi hesablayan useWatch + useMemo zənciri bir-birinə adla istinad edən üç ifadə sahəsi ilə əvəz olunur. RHF versiyasında yalnız showSubmit vasitəsilə izləmə yolu ilə yenidən qurulan baxış səhifəsi vəziyyəti, addım 3 render filialı. Və nəhayət, naviqasiya düyməsinin məntiqi səhifə obyektindəki tək visibleIf xassəsidir.
Eyni məntiq orada da var. Sadəcə olaraq, sxem ona komponentə yayılmaqdansa, təcrid olunmuş şəkildə göründüyü yerdə yaşamaq üçün yer verir. Həmçinin nəzərə alın ki, sxem aralıq, vergi və cəmi üçün type: 'ifadə' istifadə edir. İfadə yalnız oxunur və əsasən hesablanmış dəyərləri göstərmək üçün istifadə olunur. SurveyJS həmçinin statik məzmun üçün 'html' tipini dəstəkləyir, lakin hesablanmış dəyərlər üçün ifadə düzgün seçimdir. İndi Reaksiya tərəfi üçün. Render və Təqdimat Çox sadə. OnComplete-i eyni şəkildə API-yə köçürün - useMutation və ya sadə gətirmə vasitəsilə:
import { useState, useEffect, useRef } "react";import { useMutation } from "@tanstack/react-query";import { Model} from "survey-core";import { Survey} from "survey-react-ui";import "survey-core/survey-core.css";
SurveyForm() ixrac funksiyası { const [model] = useState(() => yeni Model(surveySchema));
const mutasiya = useMutation({ mutationFn: async (məlumat) => { const res = alınmasını gözləyin("/api/orders", { üsul: "POST", başlıqlar: { "Content-Type": "application/json" }, bədən: JSON.stringify (data), }); əgər (!res.ok) yeni Xəta atsa ("Göndərmək mümkün olmadı"); res.json(); }, });
const mutationRef = useRef(mutasiya); mutationRef.current = mutasiya; useEffect(() => { const handler = (göndərən) => mutationRef.current.mutate(sender.data); model.onComplete.add(handler); return () => model.onComplete.remove(handler); }, [model]); // ref hər render işləyicisini yenidən qeyd etməkdən çəkinir (mutasiya obyektinin şəxsiyyəti dəyişir)
qayıtmaq (
<>
Altıncı sönmə ilə Pen SurveyJS-03-SurveyJS [forked] baxın.
İstifadəçi son görünən səhifənin sonuna çatdıqda onComplete işə salınır. Beləliklə, əgər ümumi heç vaxt 100-ü keçməsə və nəzərdən keçirmə səhifəsi atlanarsa, o, yenə də düzgün işləyir, çünki SurveyJS “son səhifə”nin nə demək olduğuna qərar verməzdən əvvəl görmə qabiliyyətini qiymətləndirir. Sonra, sender.data birinci dərəcəli sahələr kimi hesablanmış dəyərlərlə (alt cəmi, vergi, cəmi) bütün cavabları ehtiva edir, ona görə də API yükü onSubmit-də əl ilə yığılmış RHF versiyası ilə eynidir. ThemutationRef nümunəsi, hər renderdə dəyişən dəyər üzərində sabit hadisə idarəçisinə ehtiyacınız olan hər yerdə çata biləcəyiniz nümunədir - bu barədə SurveyJS-ə xas olan heç bir şey yoxdur.
React komponenti artıq heç bir iş məntiqini ehtiva etmir. UseWatch, şərti JSX, addım sayğacı, useMemo zənciri, superRefine yoxdur. React əslində yaxşı olanı edir: komponenti göstərmək və onu API çağırışına bağlamaq. Reaksiyadan nə çıxdı?
narahatlıq RHF yığını SurveyJS Görünüş JSX filialları visibleIf Əldə edilmiş dəyərlər useWatch / useMemo ifadə Çarpaz sahə qaydaları super zərifləşdirin Sxem şərtləri Naviqasiya addım vəziyyəti Səhifə visibleIf Qayda yeri Fayllar arasında paylanmışdır Sxemdə mərkəzləşdirilmişdir
React-də qalan şey tərtibat, üslub, təqdimetmə naqilləri və tətbiq inteqrasiyasıdır, yəni React-in əslində nəzərdə tutulduğu şeylərdir. Qalan hər şey sxemə köçürüldü və sxem sadəcə JSON obyekti olduğu üçün o, verilənlər bazasında saxlanıla, tətbiq kodunuzdan asılı olmayaraq versiyaya salına və ya yerləşdirmə tələb etmədən daxili alətlər vasitəsilə redaktə edilə bilər. Baxış səhifəsini işə salan həddi dəyişdirməli olan məhsul meneceri bunu komponentə toxunmadan edə bilər. Bu, forma davranışının tez-tez inkişaf etdiyi və həmişə mühəndislər tərəfindən idarə olunmadığı komandalar üçün mənalı əməliyyat fərqidir. Hər bir yanaşmadan nə vaxt istifadə etməli? Budur mənim üçün işləyən yaxşı bir qayda: formanı tamamilə sildiyinizi təsəvvür edin. Nə itirərdiniz?
Əgər bu ekrandırsa, komponentlə idarə olunan formalar istəyirsiniz. Həqiqi qərarları kodlayan həddlər, budaqlanma qaydaları və şərti tələblər kimi iş məntiqidirsə, siz sxem mühərriki istəyirsiniz.
Eynilə, yolunuzdakı dəyişikliklər əsasən etiketlər, sahələr və düzənlə bağlıdırsa, RHF sizə yaxşı xidmət edəcəkdir. Əgər əməliyyatlar və ya hüquq komandanızın çərşənbə axşamı günortadan sonra bilet təqdim etmədən tənzimləməli ola biləcəyi şərtlər, nəticələr və qaydalar haqqındadırsa, SurveyJS ilə sxem modeli daha düzgün uyğun gəlir. Bu iki yanaşma əslində bir-biri ilə rəqabət aparmır. Onlar müxtəlif sinif problemlərini həll edir və qaçmağa dəyər səhv, abstraksiyanı məntiqin ağırlığına uyğunlaşdırmamaqdır - qayda sisteminə komponent kimi yanaşmaq, çünki bu tanış alətdir və ya forma üç addıma qədər böyüyüb şərti sahə əldə etdiyi üçün siyasət mühərrikinə çatmaq. Burada qurduğumuz forma qəsdən sərhədin yaxınlığında oturur, fərqi üzə çıxaracaq qədər mürəkkəbdir, lakin müqayisənin saxta olduğunu hiss etdirəcək qədər həddindən artıq deyil. Kod bazanızda çətinləşən əksər real formalar, yəqin ki, eyni sərhədə yaxındır və sual adətən sadəcə olaraq, onların əslində nə olduğunu kimin adlandırıb- adlandırmamasıdır. Aşağıdakı hallarda React Hook Form + Zod istifadə edin:
Formalar CRUD yönümlüdür; Məntiq dayazdır və istifadəçi interfeysi ilə idarə olunur; Mühəndislər bütün davranışlara sahibdirlər; Backend həqiqətin mənbəyi olaraq qalır.
SurveyJS-dən aşağıdakı hallarda istifadə edin:
Formalar biznes qərarlarını kodlayır; Qaydalar UI-dən asılı olmayaraq inkişaf edir; Məntiq görünən, yoxlanıla bilən və ya versiyalı olmalıdır; Mühəndis olmayanlar davranışa təsir göstərir; Eyni forma birdən çox cəbhədə işləməlidir.