Mundarija (35)
- Bu darsda
- 1. Nega bu kerak?
- 2. Birinchi forma
- 2.1 O'rnatish
- 2.2 register — maydonni ulash
- 2.3 handleSubmit — yuborish
- 2.4 Nechta render?
- 3. Tayyor qoidalar va xatolar
- 3.1 required, minLength, min, pattern
- 3.2 Fokus birinchi xatoga
- 3.3 validate — o'z qoidangiz
- 4. formState — forma holati
- 4.1 isSubmitting va isSubmitSuccessful
- 4.2 isDirty — o'zgartirildimi?
- 4.3 formState — "kim o'qisa, o'sha yangilanadi"
- 5. mode — xato qachon ko'rinadi
- 5.1 To'rt rejim
- 5.2 onTouched — muvozanat
- 6. Qachon RHF kerak emas
- 6.1 Kichik forma — React'ning o'zi yetadi
- 6.2 Ishlab turgan kodni ko'chirmaslik
- 7. Vazifalar qadami: tahrirlash formasi boshlandi (holat 32)
- 7.1 Nima quramiz
- 7.2 Marshrut
- 7.3 Forma: register va xato
- 7.4 Narxi
- 8. Ko'p uchraydigan xatolar
- 9. Mashqlar
- 1-mashq (oson): nima chiqadi?
- 2-mashq (o'rta): fikr formasi
- 3-mashq (qiyin): bron — yuborish holati bilan
- 4-mashq: Vazifalar qadami
- 10. Real ishda
- Xulosa
- Manbalar
React Hook Form: useForm, register, handleSubmit va xatolarni ko'rsatish
Qisqacha: React Hook Form (RHF) — formani uncontrolled maydonlar ustiga quradigan kutubxona.
useForm()forma "boshqaruvchisini" beradi,{...register("name")}maydonni unga ulaydi,handleSubmit(fn)yuborishda qiymatlarni yig'adi, tekshiradi va hammasi to'g'ri bo'lsafn(data)ni chaqiradi. XatolarformState.errorsda. Harf yozilganda komponent qayta chizilmaydi, shuning uchun katta forma ham tez ishlaydi.
Bu darsda
useForm,registervahandleSubmitbilan forma yozasiz varegisteraslida nima qaytarishini ko'rasiz.- Tayyor qoidalar (
required,minLength,min,pattern,validate) bilan o'zbekcha xato xabarlarini chiqarasiz. formStatedagierrors,isSubmitting,isDirty,isSubmitSuccessfulni ishlatasiz.mode(onSubmit,onBlur,onChange,onTouched) — xato qachon ko'rinishini tanlaysiz.- Qachon RHF kerak, qachon React'ning o'z
<form action>i yetarli —vazifalardagi qaror misolida.
Oldin bilishingiz kerak: Formalar I: controlled inputlar, Formalar II: uncontrolled inputlar, FormData va form action, useActionState va useFormStatus, useId.
1. Nega bu kerak?
Oldingi darslarda ilova holatini qayerda saqlashni o'rgandik: URL, server keshi, store. Endi ilovaga ma'lumot kiradigan joy — formalar.
Jasur aka «Bahor» sayti uchun stol bron qilish formasini so'radi: ism, telefon, mehmonlar soni va izoh. Sardor uni controlled inputlar bilan yozdi. Har maydonga useState, har maydonga onChange. Keyin talablar keldi:
- har maydon ostida o'z xatosi: "Ismingizni yozing", "Raqam +998 bilan boshlansin";
- xato birinchi harfdanoq "baqirmasin" — mehmon yozib bo'lgach ko'rinsin;
- yuborilayotganda tugmada "Yuborilmoqda…", ikki marta bosilmasin;
- xato bo'lsa, fokus birinchi noto'g'ri maydonga o'tsin;
- "Saqlanmagan o'zgarish bor" — mehmon biror narsa yozdimi?
To'rt maydon uchun Sardorda o'nga yaqin useState va yuz qatordan ortiq kod bo'ldi. Har harf yozilganda butun forma qayta chizildi. Har yangi forma — xuddi shu ishni boshidan.
Hayotiy o'xshatish: ofitsiant va oshxona. Yomon ofitsiant mehmon har so'z aytganda oshxonaga yuguradi: "osh dedi!", "ikkita dedi!". Yaxshisi bloknotga yozadi va oshxonaga tayyor buyurtmani olib boradi. Controlled forma — birinchi ofitsiant: har harf React'ga boradi. React Hook Form — ikkinchisi. Qiymatlar maydonning o'zida (DOM'da) turadi, kutubxona ularni kerak paytda — yuborishda yoki tekshiruvda — o'qiydi.
2. Birinchi forma
2.1 O'rnatish
RHF — oddiy dependency, versiya aniq (--save-exact, Semver darsidagi qoida):
npm install --save-exact react-hook-form@7.89.0Saytdagi natija oynasida RHF 7.89.0 allaqachon bor — misollarni «Ishga tushir» bilan sinang. Turlari paketning ichida, @types/... kerak emas.
2.2 register — maydonni ulash
Asosiy hook — useForm(). U forma bilan ishlaydigan funksiyalar to'plamini qaytaradi. Birinchisi — register (ro'yxatdan o'tkazish). Avval u nima qaytarishini ko'raylik:
import { useForm } from "react-hook-form";
export default function App() {
const { register } = useForm();
const field = register("name");
console.log(Object.keys(field));
return <input aria-label="Ism" {...field} />;
}Konsolda:
(4) ['name', 'onChange', 'onBlur', 'ref']
(4) ['name', 'onChange', 'onBlur', 'ref']Sir yo'q: register("name") — oddiy obyekt, ichida to'rtta props. (Qator ikki marta chiqdi, chunki useForm ulangandan keyin komponent bir marta qayta chiziladi — «Nechta render?» bo'limida.) {...field} (spread) ularni <input> ga yoyadi:
| Prop | Vazifasi |
|---|---|
name |
maydon nomi — natijadagi kalit |
ref |
RHF DOM elementni ushlab oladi va qiymatni undan o'qiydi |
onChange |
qiymat o'zgardi — kerak bo'lsa tekshiruv |
onBlur |
maydondan chiqildi — "tegilgan" (touched) belgisi |
value yo'qligiga e'tibor bering. Maydon uncontrolled (Formalar II): qiymatni React state emas, DOM saqlaydi. RHF unga ref orqali yetib boradi. Shuning uchun odatda {...register("…")} deb bir qatorda yoziladi.
2.3 handleSubmit — yuborish
Bron formasi — ikki maydon va tugma:
import { useForm } from "react-hook-form";
export default function App() {
const { register, handleSubmit } = useForm();
function book(data) {
console.log(data);
}
return (
<form onSubmit={handleSubmit(book)}>
<label htmlFor="name">Ism</label>
<input id="name" {...register("name")} />
<label htmlFor="guests">Mehmonlar soni</label>
<input id="guests" type="number" {...register("guests")} />
<button>Bron qilish</button>
</form>
);
}Konsolda:
{name: 'Malika', guests: '4'}handleSubmit(book) — "o'rovchi": u yangi funksiya qaytaradi va o'sha onSubmit ga beriladi. Forma yuborilganda u:
event.preventDefault()ni chaqiradi — sahifa qayta yuklanmaydi;- hamma ro'yxatdagi maydonlardan qiymatni yig'adi;
- qoidalarni tekshiradi (hozir qoida yo'q);
- hammasi to'g'ri bo'lsa —
book(data)ni chaqiradi.
data — maydon nomlari kalit bo'lgan obyekt. FormData bilan solishtiring: Object.fromEntries(new FormData(form)) ham deyarli shunday edi. Farqi keyingi bo'limlarda — tekshiruv, xatolar va holatlar.
Konsoldagi guests: '4' ga qarang — satr. type="number" bo'lsa ham brauzer maydon qiymatini doim satr qilib beradi. Son kerak bo'lsa — register ning ikkinchi argumenti:
import { useForm } from "react-hook-form";
export default function App() {
const { register, handleSubmit } = useForm();
return (
<form onSubmit={handleSubmit((data) => console.log(data))}>
<label htmlFor="guests">Mehmonlar soni</label>
<input
id="guests"
type="number"
{...register("guests", { valueAsNumber: true })}
/>
<button>Bron qilish</button>
</form>
);
}Konsolda:
{guests: 4}valueAsNumber: true — input.valueAsNumber dan o'qiladi. Sana uchun valueAsDate ham bor. Bo'sh maydonda valueAsNumber — NaN bo'ladi. Buni «Tayyor qoidalar» bo'limida required bilan to'samiz.
Maslahat: Nuqtali nom (
"guest.name","items.0.title") ichma-ich obyekt yasaydi:{ guest: { name: "…" } }. Massiv maydonlari — RHF III darsida.
2.4 Nechta render?
"Harf yozilganda qayta chizilmaydi" — o'lchab ko'ramiz. Avval controlled variant. Hisoblagich komponentdan tashqarida, har chizilishda bittaga oshadi. Mehmon "Malika" ni harfma-harf yozdi — olti harf:
import { useState } from "react";
let renders = 0;
export default function App() {
const [name, setName] = useState("");
renders += 1;
console.log("render", renders);
return (
<form>
<label htmlFor="name">Ism</label>
<input
id="name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</form>
);
}Konsolda:
render 1
render 2
render 3
render 4
render 5
render 6
render 7Endi o'sha forma RHF bilan:
import { useForm } from "react-hook-form";
let renders = 0;
export default function App() {
const { register } = useForm();
renders += 1;
console.log("render", renders);
return (
<form>
<label htmlFor="name">Ism</label>
<input id="name" {...register("name")} />
</form>
);
}Konsolda:
render 1
render 2Olti harf: controlled formada olti qo'shimcha render, RHF da — nol. RHF dagi ikkinchi render harfdan emas: useForm birinchi chizishdan keyin o'z holatini bir marta yangilaydi (harf yozmasdan ochsangiz ham render 2 chiqadi). Bitta maydonda farq sezilmaydi. Lekin 30 maydonli anketada, har maydon ostida murakkab komponent bo'lsa, controlled forma har harfda hammasini qayta chizadi. Qayta render sabablari darsidagi qoida: state o'zgardi — komponent va uning bolalari qayta chiziladi. RHF state'ni o'zgartirmaydi.
Jarayon butunligicha:
flowchart TD
A["Harf yozildi"] --> B["Qiymat DOM'da<br/>(render yo'q)"]
B --> C["Yuborish"]
C --> D["handleSubmit:<br/>qiymatlarni o'qiydi"]
D --> E{"Qoidalar<br/>bajarildimi?"}
E -- "ha" --> F["book(data)"]
E -- "yo'q" --> G["errors to'ldi,<br/>render, fokus"]Render faqat "natija o'zgarganda" bo'ladi: xato paydo bo'ldi yoki yo'qoldi, yuborish boshlandi yoki tugadi.
Tekshirib ko'ring:
register("name")qaytargan obyektdavalueyo'q. UndahandleSubmitqiymatni qayerdan oladi?
Javob
DOM elementning o'zidan. register qaytargan ref ni React elementga ulaydi va RHF elementni qo'lida ushlaydi. Yuborishda u input.value ni (yoki valueAsNumber bo'lsa — input.valueAsNumber ni) o'qiydi. Shuning uchun {...register("name")} ni o'z komponentingizga bersangiz, u ref ni haqiqiy <input> gacha yetkazishi shart — aks holda RHF elementni "ko'rmaydi". Bu masala RHF II darsida, Controller bilan yechiladi.
3. Tayyor qoidalar va xatolar
3.1 required, minLength, min, pattern
register ning ikkinchi argumenti — qoidalar ham. Ular HTML atributlariga o'xshaydi (brauzer validatsiyasi), lekin xabarni siz yozasiz:
| Qoida | Nimani tekshiradi | Misol |
|---|---|---|
required |
bo'sh emas | required: "Ismingizni yozing" |
minLength / maxLength |
satr uzunligi | { value: 2, message: "…" } |
min / max |
son chegarasi | { value: 1, message: "…" } |
pattern |
muntazam ifoda | { value: /^\+998/, message: "…" } |
validate |
o'z funksiyangiz | (v) => … || "xabar" |
Qoida qiymati ikki ko'rinishda bo'ladi. Qisqa: required: "Xabar". To'liq: minLength: { value: 2, message: "Xabar" }. Xabar yozilmasa (required: true), xato baribir bo'ladi, faqat message bo'sh satr.
Xato topilsa, u formState.errors ga tushadi: errors.name — { type, message }. Endi bron formasi uchala qoidasi bilan. Tekshiruvchi avval bo'sh formani yuboradi, keyin noto'g'ri qiymatlar yozib, yana yuboradi:
import { useForm } from "react-hook-form";
export default function App() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
return (
<form noValidate onSubmit={handleSubmit((d) => console.log(d))}>
<label htmlFor="name">Ism</label>
<input
id="name"
aria-invalid={errors.name ? true : undefined}
{...register("name", {
required: "Ismingizni yozing",
minLength: { value: 2, message: "Ism kamida 2 harf" },
})}
/>
{errors.name && <p role="alert">{errors.name.message}</p>}
<label htmlFor="phone">Telefon</label>
<input
id="phone"
type="tel"
placeholder="+998 90 000 00 00"
aria-invalid={errors.phone ? true : undefined}
{...register("phone", {
required: "Telefon raqamini yozing",
pattern: {
value: /^\+998 \d{2} \d{3} \d{2} \d{2}$/,
message: "Namuna: +998 90 000 00 00",
},
})}
/>
{errors.phone && <p role="alert">{errors.phone.message}</p>}
<label htmlFor="guests">Mehmonlar soni</label>
<input
id="guests"
type="number"
aria-invalid={errors.guests ? true : undefined}
{...register("guests", {
valueAsNumber: true,
required: "Nechta mehmon?",
min: { value: 1, message: "Kamida 1 kishi" },
max: { value: 12, message: "Ko'pi bilan 12 kishi" },
})}
/>
{errors.guests && <p role="alert">{errors.guests.message}</p>}
<button>Bron qilish</button>
</form>
);
}Bo'sh forma yuborilganda har maydon ostida o'z xabari chiqdi: "Ismingizni yozing", "Telefon raqamini yozing", "Nechta mehmon?" (tekshiruvchi uchalasini oraliq nuqtada ko'rdi). Keyin noto'g'ri qiymatlar bilan:
Ekranda:
Ism
Ism kamida 2 harf
Telefon
Namuna: +998 90 000 00 00
Mehmonlar soni
Ko'pi bilan 12 kishi
Bron qilishHar qoida o'z xabarini berdi. Bitta maydonda bir nechta qoida buzilsa, errors da birinchisi turadi (qoidalar required → uzunlik → chegara → pattern → validate tartibida tekshiriladi).
Konsol ikki safar ham bo'sh — console.log(d) chaqirilmadi: xato bor ekan, handleSubmit ichkari funksiyani chaqirmaydi. Uch narsaga e'tibor bering:
noValidate— brauzerning o'z "pufakchalarini" o'chiradi (brauzer validatsiyasi). Aks holdatype="tel"kabi atributlar bo'yicha brauzer ham tekshirib, o'z inglizcha xabarini chiqarishi mumkin edi. Xabarlar bitta joydan — RHF'dan kelsin.aria-invalid— Malika (ekran o'quvchi) maydonga kelganda "noto'g'ri" ni eshitadi.undefined— atribut umuman yozilmaydi.role="alert"— xato paydo bo'lganda ekran o'quvchi uni darhol o'qiydi (accessibility asoslari).
3.2 Fokus birinchi xatoga
Xato bilan yuborilganda RHF fokusni birinchi xatoli maydonga o'tkazadi (shouldFocusError: true — sukut). Bobur (faqat klaviatura) uchun bu juda muhim: u xatoni izlab Tab bosib yurmaydi. Fokusni tekshirish uchun alohida tugma qo'shdik:
import { useForm } from "react-hook-form";
export default function App() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
return (
<form noValidate onSubmit={handleSubmit(() => {})}>
<label htmlFor="name">Ism</label>
<input id="name" {...register("name", { required: true })} />
<label htmlFor="phone">Telefon</label>
<input id="phone" {...register("phone", { required: true })} />
{errors.phone && <p role="alert">Telefon kerak</p>}
<button>Bron qilish</button>
<button
type="button"
onClick={() => console.log(document.activeElement.id)}
>
Fokus qayerda?
</button>
</form>
);
}Konsolda:
phoneIsm to'ldirilgan, telefon bo'sh — fokus phone da. required: true — xabarsiz qoida; xabarni bu safar JSX'ning o'zida yozdik. Bunday maydonda ref ulanmagan bo'lsa (masalan, o'z komponentingiz ref ni o'tkazmasa), fokus ham o'tmaydi — yana bir sabab ref ni yo'qotmaslikka.
3.3 validate — o'z qoidangiz
Tayyor qoidalar yetmasa — validate. Funksiya true qaytarsa — to'g'ri, satr qaytarsa — o'sha satr xato xabari bo'ladi:
import { useForm } from "react-hook-form";
export default function App() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
return (
<form noValidate onSubmit={handleSubmit((d) => console.log(d))}>
<label htmlFor="name">Ism</label>
<input
id="name"
{...register("name", {
validate: {
noDigits: (v) => !/\d/.test(v) || "Ismda raqam bo'lmasin",
notAdmin: (v) => v !== "admin" || "Bu ism band",
},
})}
/>
{errors.name && (
<p role="alert">
{errors.name.type}: {errors.name.message}
</p>
)}
<button>Bron qilish</button>
</form>
);
}Ekranda:
Ism
noDigits: Ismda raqam bo'lmasin
Bron qilishvalidate bitta funksiya ham, nomlangan funksiyalar obyekti ham bo'lishi mumkin. Obyektda kalit nomi errors.name.type ga tushadi — testda yoki tahlilda qaysi qoida ishlaganini bilish uchun qulay. !/\d/.test(v) || "…" — mantiqiy operatorlar darsidagi ||: chap tomoni true bo'lsa o'sha, aks holda xabar.
Funksiya async ham bo'lishi mumkin — masalan, serverdan "bu nom bandmi?" deb so'rash. Lekin har harfda so'rov ketmasligi uchun ehtiyotkorlik kerak. Bu RHF III darsida.
Tekshirib ko'ring:
max: 12qoidasi bor, mehmon maydonga"12"yozdi, lekinvalueAsNumberyo'q. Xato chiqadimi?
Javob
Yo'q, 12 to'g'ri — RHF min/max ni tekshirishda satrni ham songa aylantirib solishtiradi. Lekin data.guests baribir "12" — satr bo'lib keladi. Keyin kodda data.guests + 1 deb yozsangiz, "121" chiqadi. Tekshiruv va natija turi — ikki alohida masala. Son kerak bo'lsa — valueAsNumber: true. Kelgusi darsda Zod bilan bu ikkalasini bitta sxemada hal qilamiz.
4. formState — forma holati
4.1 isSubmitting va isSubmitSuccessful
Bron serverga boradi, bu vaqt oladi. handleSubmit ga async funksiya berilsa, RHF uning tugashini kutadi va shu orada isSubmitting — true. Server o'rniga 300 ms kutadigan soxta funksiya:
import { useForm } from "react-hook-form";
function wait(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export default function App() {
const {
register,
handleSubmit,
formState: { isSubmitting, isSubmitSuccessful },
} = useForm();
async function book(data) {
await wait(300);
console.log("saqlandi:", data.name);
}
return (
<form onSubmit={handleSubmit(book)}>
<p>
<label htmlFor="name">Ism</label>
<input id="name" {...register("name")} />
</p>
<button disabled={isSubmitting}>
{isSubmitting ? "Yuborilmoqda…" : "Bron qilish"}
</button>
{isSubmitSuccessful && <p role="status">Bron qabul qilindi</p>}
</form>
);
}Konsolda:
saqlandi: JasurEkranda:
Ism
Bron qilish
Bron qabul qilindiTekshiruvchi bosishdan keyin ekranda "Yuborilmoqda…" ni ko'rdi (oraliq holat), 300 ms dan keyin esa yakuniy holatni. Bu — useActionState dagi isPending ning RHF'dagi juftligi.
disabled={isSubmitting} — ikki marta bosishdan himoya. RHF buni o'zi qilmaydi: yuborish paytida tugma yana bosilsa, book ikkinchi marta chaqiriladi (biz sinadik — konsolda ikkita "saqlandi"). Lekin bir nozik joy bor. O'chirilgan (disabled) tugmaga Tab bilan borilmaydi va ekran o'quvchi uni "o'chirilgan" deydi, sababini aytmaydi. Shuning uchun vazifalar dagi tahrir formasida tugma o'chirilmaydi — faqat matni o'zgaradi (pastda, «Vazifalar qadami»da). Tugma faol qolsa, takroriy yuborishni boshqa yo'l bilan to'xtatish kerak — 3-mashqda.
isSubmitSuccessful — oxirgi yuborish xatosiz tugadimi. book ichida xato tashlansa (throw), u false qoladi.
4.2 isDirty — o'zgartirildimi?
isDirty — kamida bitta maydon boshlang'ich qiymatidan farq qiladimi. Boshlang'ich qiymat — useForm({ defaultValues }) dan. Mehmon izoh yozdi, keyin o'chirib tashladi:
import { useForm } from "react-hook-form";
export default function App() {
const {
register,
formState: { isDirty, dirtyFields },
} = useForm({ defaultValues: { note: "" } });
return (
<form>
<label htmlFor="note">Izoh</label>
<input id="note" {...register("note")} />
<p>isDirty: {String(isDirty)}</p>
<p>
O'zgargan: {Object.keys(dirtyFields).join(", ") || "yo'q"}
</p>
</form>
);
}Ekranda:
Izoh
isDirty: false
O'zgargan: yo'qYozilgach — true, matn o'chirilgach yana false: qiymat boshlang'ichiga qaytdi. RHF "biror marta tegildimi" ni emas, "hozir farq qiladimi" ni so'raydi. dirtyFields — aynan qaysi maydonlar o'zgargan. U tahrirlash formasida faqat o'zgarganini serverga yuborish uchun kerak bo'ladi (RHF II).
Diqqat:
isDirtyuchundefaultValuesshart. Ularsiz RHF "boshlang'ich holat"ni bilmaydi va natija kutilmagan bo'lishi mumkin. Qoida: har formagadefaultValuesbering — bo'sh maydon uchun"".
4.3 formState — "kim o'qisa, o'sha yangilanadi"
formState dagi qiymatlar ko'p: errors, isSubmitting, isDirty, dirtyFields, touchedFields, isValid, submitCount, isSubmitSuccessful. Ularning hammasi o'zgarganda komponent qayta chizilsa, "kam render" va'dasi buzilardi.
Shuning uchun formState — Proxy (Proxy darsida o'tgan "kuzatuvchi" obyekt). RHF render paytida qaysi maydon o'qilganini eslab qoladi va faqat o'shalar o'zgarganda qayta chizadi. Yuqoridagi misolda faqat isDirty va dirtyFields o'qildi — xatolar o'zgarsa, bu komponent qayta chizilmaydi.
Bundan bitta qoida chiqadi: kerakli qiymatni render paytida o'qing (destrukturlash bilan, yuqoridagidek). Faqat hodisa funksiyasida formState.isDirty deb o'qisangiz, RHF unga "obuna" bo'lganingizni bilmaydi va qiymat eskirgan bo'lishi mumkin.
| Qiymat | Ma'nosi |
|---|---|
errors |
maydon → { type, message } |
isSubmitting |
handleSubmit dagi funksiya hali tugamagan |
isSubmitSuccessful |
oxirgi yuborish xatosiz tugadi |
isDirty / dirtyFields |
boshlang'ich qiymatdan farq bormi / qaysilarida |
touchedFields |
qaysi maydondan bir marta chiqilgan (blur) |
isValid |
hozir xato yo'qmi (o'qilsa — har o'zgarishda tekshiradi) |
submitCount |
necha marta yuborishga urinildi |
isValid ga ehtiyot bo'ling: uni o'qisangiz, RHF uni yangilab turish uchun har o'zgarishda tekshiruvni ishga tushiradi. "Xato bo'lsa tugma o'chsin" uchun uni ishlatish o'rniga, odatda tugmani faol qoldirib, yuborishda xatolarni ko'rsatish yaxshiroq.
Tekshirib ko'ring:
defaultValues: { note: "Deraza yonida" }. Mehmon matnni o'chirib, qaytadan aynan "Deraza yonida" deb yozdi.isDirtynima?
Javob
false. RHF joriy qiymatni boshlang'ichi bilan solishtiradi, tarixni emas. Matn boshlang'ich bilan bir xil — demak, "o'zgarish yo'q". Bu foydali: tahrirlash formasida "Saqlash" bosilganda isDirty — false bo'lsa, serverga so'rov yubormaslik mumkin. vazifalar aynan shunday qiladi.
5. mode — xato qachon ko'rinadi
5.1 To'rt rejim
Sukut bo'yicha RHF faqat yuborishda tekshiradi. Bu mode: "onSubmit". Birinchi yuborishdan keyin esa xatoli maydon har o'zgarishda qayta tekshiriladi — xato mehmon to'g'rilagan zahoti yo'qoladi. Bu ikkinchi sozlama — reValidateMode: "onChange" (sukut).
mode |
Birinchi tekshiruv | Qachon yaxshi |
|---|---|---|
onSubmit (sukut) |
yuborishda | ko'pchilik formalar |
onBlur |
maydondan chiqqanda | uzun anketa, har maydon alohida |
onTouched |
birinchi chiqishda, keyin har o'zgarishda | tahrirlash formasi (vazifalar) |
onChange |
har harfda | parol kuchi kabi "jonli" maslahat |
all |
chiqishda ham, har harfda ham | kam ishlatiladi |
onChange ni sinaymiz. Mehmon bitta harf yozdi — xato darhol chiqdi, uchinchi harfda yo'qoldi:
import { useForm } from "react-hook-form";
export default function App() {
const {
register,
formState: { errors },
} = useForm({ mode: "onChange" });
return (
<form>
<label htmlFor="name">Ism</label>
<input
id="name"
{...register("name", {
minLength: { value: 2, message: "Ism kamida 2 harf" },
})}
/>
<p role="status">{errors.name?.message ?? "Hammasi joyida"}</p>
</form>
);
}Ekranda:
Ism
Hammasi joyidaQulaymi? Mehmon ismini endi yoza boshladi, forma esa unga "noto'g'ri!" deb baqiryapti. Shuning uchun onChange — faqat jonli maslahat uchun. Odatiy formada xato "yozib bo'lgach" ko'rinsin.
5.2 onTouched — muvozanat
onTouched — mehmon maydondan chiqqanda birinchi marta tekshiradi. Keyin, mehmon xatoni tuzatayotganda, har harfda qayta tekshiradi va xato tuzatilgan zahoti yo'qoladi. Ya'ni "erta baqirmaydi, kech kechirmaydi".
Maydondan chiqish — blur hodisasi. Saytdagi misolda Tab bosib ko'ring. Tekshiruvchimiz klaviatura bosa olmaydi, shuning uchun quyidagi tugma fokusni keyingi maydonga o'tkazadi — Tab bosgandek:
import { useForm } from "react-hook-form";
export default function App() {
const {
register,
formState: { errors },
} = useForm({ mode: "onTouched" });
return (
<form>
<p>
<label htmlFor="name">Ism</label>
<input
id="name"
autoFocus
{...register("name", {
minLength: { value: 2, message: "Ism kamida 2 harf" },
})}
/>
</p>
<p>
<label htmlFor="phone">Telefon</label>
<input id="phone" />
</p>
<p role="status">{errors.name?.message ?? "Hammasi joyida"}</p>
<button
type="button"
onClick={() => document.getElementById("phone").focus()}
>
Keyingi maydon
</button>
</form>
);
}Ekranda:
Ism
Telefon
Hammasi joyida
Keyingi maydonUch holat ketma-ket:
| Qadam | Ism maydoni | Ekranda |
|---|---|---|
| "A" yozildi | hali tegilmagan — tekshiruv yo'q | Hammasi joyida |
| «Keyingi maydon» — fokus ketdi (blur) | tegildi — tekshirildi | Ism kamida 2 harf |
| "Ali" yozildi | tegilgan — har o'zgarishda tekshiriladi | Hammasi joyida |
Uchinchi qatorga qarang: xato darhol yo'qoldi, garchi fokus hali ham telefonda bo'lsa ham. Mana shu onTouched ning ikkinchi yarmi.
stateDiagram-v2
[*] --> Jim: yozilmoqda
Jim --> Tekshirildi: maydondan chiqildi
Jim --> Tekshirildi: yuborildi
Tekshirildi --> Tekshirildi: har o'zgarishdaTekshirib ko'ring:
mode: "onSubmit"(sukut). Mehmon bo'sh formani yubordi — xato chiqdi. Keyin ismni yoza boshladi. Xato qachon yo'qoladi?
Javob
Birinchi to'g'ri harfdayoq (agar qoida required bo'lsa). Birinchi yuborishdan keyin reValidateMode ishlaydi, sukuti — "onChange". Ya'ni "yuborishgacha jim, yuborgandan keyin — jonli". Agar reValidateMode: "onBlur" qo'yilsa, xato faqat maydondan chiqqanda yo'qoladi. vazifalar dagi ko'p qadamli forma shunday qiladi — har harfda serverga so'rov ketmasin (RHF III).
6. Qachon RHF kerak emas
6.1 Kichik forma — React'ning o'zi yetadi
RHF — kutubxona, ya'ni narxi bor. vazifalar da uning ulushi (Zod tekshiruvlari bilan) — +14.7 KB gzip (pastda, o'lchov bilan). Bitta maydonli "qidirish" yoki "obuna" formasi uchun bu ortiqcha. React 19 ning o'zida forma vositalari bor: <form action>, useActionState, useFormStatus (Actions, useActionState).
| Vaziyat | Tanlov |
|---|---|
| 1–2 maydon, bitta xato xabari | <form action> + useActionState |
har maydonda o'z xatosi, isDirty, fokus |
RHF |
| maxsus komponentlar (switch, combobox, sana) | RHF + Controller |
| ro'yxat maydonlari, ko'p qadamli forma | RHF (useFieldArray, trigger) |
| Next.js server action, JS'siz ham ishlashi kerak | <form action> (yoki Conform — muqobillar) |
6.2 Ishlab turgan kodni ko'chirmaslik
vazifalar da ro'yxat sahifasining yuqorisida "tezkor forma" bor: bitta matn maydoni va «Qo'shish» tugmasi. U Actions darsidan beri React'ning <form action> ida ishlaydi. Endi RHF o'rnatilyapti — uni ham ko'chiramizmi?
Biz ko'chirmadik. Sabablar:
- Ishlaydi va tekshirilgan. 13–17-qismlardan qolgan 159 ta brauzer tekshiruvining ~40 tasi aynan shu formaga tayanadi: qoralama, teg takliflari, xato matnlari, fokus. Ko'chirish — shu 40 tasining har birini qayta sinash.
- Foyda yo'q. Bitta maydon, bitta xato. RHF ning kuchli tomonlari (ko'p maydon,
isDirty, dinamik ro'yxat) bu yerda kerak emas. - Xavf bor. Xulq biroz o'zgaradi (masalan, xato qachon ko'rinadi) — foydalanuvchi bunga o'rgangan.
Qaror: RHF + Zod — faqat yangi formalarda. Tahrirlash formasi (shu va keyingi uch dars), ko'p qadamli forma (RHF III) va teg combobox'i (Maxsus inputlar). Real loyihada ham shunday: "yangi kutubxona keldi — hamma eski kodni ko'chiramiz" deyish xavfli. Yangi kod yangi usulda, eskisi — tegish kerak bo'lgandagina.
7. Vazifalar qadami: tahrirlash formasi boshlandi (holat 32)
7.1 Nima quramiz
vazifalar da vazifani tahrirlash ro'yxat qatorining ichida, bitta maydonda bo'lar edi. Endi vazifa sahifasida alohida bo'lim: /vazifa/2/tahrir. Unda matn maydoni, "bajarildi" almashtirgichi, jonli teglar va server xatosi chiqadi.
Bu forma to'rt darsda yig'iladi va commit — to'rtinchisida (RHF II):
| Dars | Formaga nima qo'shiladi |
|---|---|
| shu dars | useForm, register, errors, noValidate, a11y |
| Zod chuqurroq | sxema: trim, uzunlik, takror (refine) |
| RHF + Zod | zodResolver, defaultValues, server xatosi |
| RHF II | Controller (almashtirgich), useWatch, useOrqaga, commit |
git switch -c feat/tahrir-formasi
npm install --save-exact react-hook-form@7.89.0 \
@hookform/resolvers@5.9.1@hookform/resolvers — RHF ni sxema kutubxonalariga ulaydigan paket. U ikki darsdan keyin kerak bo'ladi, lekin kanonda ikkalasi bitta qadamda o'rnatilgan. package.json (kanon):
"dependencies": {
+ "@hookform/resolvers": "5.9.1",
"@tanstack/react-query": "5.104.1",
"react": "19.3.0",
"react-dom": "19.3.0",
+ "react-hook-form": "7.89.0",
"react-router": "8.4.0",7.2 Marshrut
Tahrir — vazifa sahifasining ichma-ich bo'limi (React Router II), marshrutlar.tsx da (kanon):
+import { VazifaTahriri } from "./sahifalar/VazifaTahriri.tsx";
...
element: <VazifaUmumiy />,
action: vazifaAction,
},
+ // 19/#29–#32: tahrir formasi (React Hook Form + Zod)
+ {
+ path: "tahrir",
+ element: <VazifaTahriri />,
+ handle: nomi("Tahrirlash"),
+ },handle: nomi(…) — non yo'li (breadcrumb) uchun bo'lim nomi. Non yo'li endi "Ro'yxat / Non olish #bozor / Tahrirlash" bo'ladi.
7.3 Forma: register va xato
VazifaTahriri.tsx ning boshidagi izoh — bugungi darsning qisqa mazmuni (kanon, izoh qatorlari telefonga sig'ishi uchun qisqartirilgan):
// VazifaTahriri.tsx — "/vazifa/:id/tahrir" (19/#29–#32):
// React Hook Form + Zod. Ro'yxat sahifasidagi formalar —
// React'ning o'z <form action> (18/#05); bu yerda — ko'p
// maydonli forma: har maydonning xatosi, "o'zgardimi" (isDirty),
// jonli ko'rinish (useWatch), boshqariladigan maxsus element
// (Controller) va server xatosi (setError "root") — RHF shularni
// beradi.
// register — uncontrolled: harf yozilganda React qayta CHIZMAYDIBugun o'rganganimiz — formaning "skeleti". useForm dan olinadigan qismlar (kanon):
const {
register,
control,
handleSubmit,
setError,
formState: { errors, isDirty, isSubmitting },
} = useForm({
// ...
// Xato — maydondan chiqqanda (blur), keyin — har o'zgarishda
mode: "onTouched",
});control va setError — keyingi darslarda. formState dan faqat uchtasi o'qiladi — Proxy shu uchtasiga obuna bo'ladi. mode: "onTouched" — «Muvozanat» bo'limidagi rejim.
Matn maydoni va uning xatosi (kanon):
const matnXatosi = errors.matn?.message;
return (
// noValidate: brauzerning o'z pufakchalari emas — sxema
// xabarlari
<form
id="tahrir-forma"
noValidate
onSubmit={(event) => void handleSubmit(saqla)(event)}
>
<p>
<label htmlFor="tahrir-matn">Matn</label>
<input
id="tahrir-matn"
autoComplete="off"
aria-invalid={matnXatosi === undefined ? undefined : true}
aria-describedby={
matnXatosi === undefined ? undefined : xatoId
}
{...register("matn")}
/>
</p>
<p id={xatoId} className="xato" role="status">
{matnXatosi}
</p>Bugungi darsdan farqlar:
aria-describedby— xato matnini maydonga bog'laydi: Malika maydonga kelganda ekran o'quvchi nomidan keyin xatoni ham o'qiydi.xatoId— useId dan, sahifada noyob.role="status"(alertemas) — xato xushmuomala e'lon qilinadi, ekran o'quvchi gapini bo'lmaydi. Forma yuborilganda fokus baribir maydonga o'tadi,aria-describedbyxatoni o'qitadi.void handleSubmit(saqla)(event)—handleSubmitqaytargan funksiya Promise qaytaradi. ESLint'ningno-misused-promisesqoidasi (ESLint II)onSubmitga Promise qaytaradigan funksiyani bermaslikni talab qiladi.void— "natijasini ataylab kutmayman" degani.register("matn")ga qoida yo'q. Qoidalar bu formadaregisterda emas, sxemada bo'ladi — kelgusi darsda.
Yuborish tugmasi (kanon):
{/* disabled emas: o'chirilgan tugmaga Tab bilan borilmaydi
va sababi aytilmaydi. O'zgarish yo'q bo'lsa — saqla() o'zi
qaytadi */}
<Tugma type="submit" variant="asosiy">
{isSubmitting ? "Saqlanmoqda…" : "Saqlash"}
</Tugma>«isSubmitting va isSubmitSuccessful» bo'limidagi tanlovning ikkinchi yo'li: tugma o'chmaydi, matni o'zgaradi.
7.4 Narxi
Holat 22 (Zustand II) va holat 32 ning build natijasi (kanon natijalar/tekshiruv-*.txt):
holat 22: dist/assets/index-BjV6WuHu.js 421.00 kB │ gzip: 132.77 kB
holat 32: dist/assets/index-DNMuYLGJ.js 463.17 kB │ gzip: 147.55 kBVite hisobida +14.8 KB gzip — RHF, resolvers va formaning Zod tekshiruvlari birga. Qismning boshqa darslarida bu raqam +14.7 KB: u yerda JS va CSS birga gzip -9 bilan o'lchangan (bitta usul — taqqoslash uchun). Bu 19-qismdagi ikkinchi eng katta qo'shimcha (birinchisi — router, +29.4). Kichik formaga kutubxona qo'shmaslik qarori shundan.
Bugun commit yo'q — fayl hali yarim. Tekshiruvlar, testlar va commit RHF II darsida.
8. Ko'p uchraydigan xatolar
Eng ko'p uchraydigani — xatosi bo'lmagan maydonning message iga murojaat qilish:
import { useForm } from "react-hook-form";
export default function App() {
const {
register,
formState: { errors },
} = useForm();
return (
<form>
<input aria-label="Ism" {...register("name")} />
<p>{errors.name.message}</p>
</form>
);
}Konsolda:
Uncaught TypeError: Cannot read properties of undefined (reading 'message')Tarjimasi: "undefined ning message xususiyatini o'qib bo'lmaydi". Xato bo'lmasa, errors.name umuman yo'q (undefined). Davosi — errors.name?.message (optional chaining) yoki errors.name && ….
| Belgi | Sabab | Davosi |
|---|---|---|
data da maydon yo'q |
{...register("x")} yozilmagan yoki register faqat chaqirilgan, yoyilmagan |
<input {...register("x")} /> |
data.guests + 1 — "41" |
qiymat satr | valueAsNumber: true (yoki sxemada coerce) |
| forma yuborilganda sahifa yangilanadi | onSubmit={book} — handleSubmit siz |
onSubmit={handleSubmit(book)} |
handleSubmit(book()) — darhol chaqiriladi |
funksiya emas, natijasi berildi | handleSubmit(book) (qavssiz) |
| ingliz tilidagi brauzer "pufakchasi" | noValidate yo'q, atributlar bor |
<form noValidate> |
isDirty doim false yoki g'alati |
defaultValues berilmagan |
useForm({ defaultValues: {…} }) |
| xato yangilanmaydi | formState faqat hodisa ichida o'qilgan |
render paytida destrukturlang |
| o'z komponentingizda qiymat yo'q, fokus o'tmaydi | komponent ref ni <input> ga yetkazmagan |
ref ni prop sifatida o'tkazing (ref prop) yoki Controller |
9. Mashqlar
Mashqlar kurs/mashqlar/19/29-rhf/ da. Har yechimni «Ishga tushir» bilan sinang.
1-mashq (oson): nima chiqadi?
Forma: useForm({ defaultValues: { name: "", guests: "" } }), ism maydoni {...register("name")}, mehmonlar — {...register("guests")} (valueAsNumber siz). Mehmon "Ali" va "3" ni yozib yubordi.
register("name")qaytargan obyektda nechta kalit bor?data.guestsning turi (typeof)?data.guests + 1nima beradi?- Hech qanday
modeberilmasa, birinchi tekshiruv qachon?
Yechim
- 4 —
onChange,onBlur,name,ref.valueyo'q: maydon uncontrolled. string— brauzer maydon qiymatini doim satr beradi,type="number"bo'lsa ham."31"— satrga son qo'shilsa, satrlar birlashadi (arifmetik operatorlar). Son uchunvalueAsNumber: true.onSubmit— sukut. Birinchi yuborishdan keyinreValidateMode: "onChange"ishlaydi.
Yechim fayli (1-yechim.jsx) shu formani to'ldirib yuboradi va dastlabki uchta javobni konsolga chiqaradi: 4, string, 31.
2-mashq (o'rta): fikr formasi
«Bahor» saytiga "Fikr bildiring" formasi kerak. Boshlang'ich kodda maydonlar bor, qoidalar yo'q. Qo'shing:
name— majburiy: "Ismingizni yozing".rating— son (valueAsNumber), 1 dan 5 gacha: "Baho 1 dan 5 gacha".comment— ko'pi bilan 200 belgi: "Fikr 200 belgidan oshmasin".- Har xato maydon ostida,
role="alert"bilan; formadanoValidate.
Ishora: «Tayyor qoidalar va xatolar» bo'limidagi bron formasi.
import { useForm } from "react-hook-form";
export default function App() {
const { register, handleSubmit } = useForm({
defaultValues: { name: "", rating: "", comment: "" },
});
return (
<form onSubmit={handleSubmit((d) => console.log(d))}>
<label htmlFor="name">Ism</label>
<input id="name" {...register("name")} />
<label htmlFor="rating">Baho</label>
<input id="rating" type="number" {...register("rating")} />
<label htmlFor="comment">Fikr</label>
<textarea id="comment" {...register("comment")} />
<button>Yuborish</button>
</form>
);
}Konsolda:
{name: 'Malika', rating: '5', comment: "Osh zo'r!"}rating: '5' — satr. Yechimdan keyin u 5 bo'lishi kerak. comment qo'shtirnoqda, chunki matnda apostrof bor.
Yechim
import { useForm } from "react-hook-form";
export default function App() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
defaultValues: { name: "", rating: "", comment: "" },
});
return (
<form noValidate onSubmit={handleSubmit((d) => console.log(d))}>
<label htmlFor="name">Ism</label>
<input
id="name"
{...register("name", { required: "Ismingizni yozing" })}
/>
{errors.name && <p role="alert">{errors.name.message}</p>}
<label htmlFor="rating">Baho</label>
<input
id="rating"
type="number"
{...register("rating", {
valueAsNumber: true,
min: { value: 1, message: "Baho 1 dan 5 gacha" },
max: { value: 5, message: "Baho 1 dan 5 gacha" },
})}
/>
{errors.rating && <p role="alert">{errors.rating.message}</p>}
<p>
<label htmlFor="comment">Fikr</label>
<textarea
id="comment"
{...register("comment", {
maxLength: {
value: 200,
message: "Fikr 200 belgidan oshmasin",
},
})}
/>
</p>
{errors.comment && (
<p role="alert">{errors.comment.message}</p>
)}
<button>Yuborish</button>
</form>
);
}Ekranda (ism bo'sh, baho 7):
Ism
Ismingizni yozing
Baho
Baho 1 dan 5 gacha
Fikr
YuborishTo'g'ri qiymatlar bilan (yoz("Ism", "Malika") yoz("Baho", "5")) konsolda {name: 'Malika', rating: 5, comment: ''} — rating endi son. min va max ga bir xil xabar berildi: mehmonga "qaysi chegara buzilgani" emas, "to'g'ri oraliq" muhim.
3-mashq (qiyin): bron — yuborish holati bilan
Bron formasini yakunlang:
name— majburiy, kamida 2 harf.guests— son, 1–12.book— async: 300 ms kutadi (wait), keyin konsolgabron: <ism>, <son> kishi.- Yuborilayotganda tugma matni "Yuborilmoqda…", tugma o'chmaydi. Takroriy bosish
bookni ikkinchi marta chaqirmasin. - Muvaffaqiyatdan keyin:
<p role="status">Rahmat, <ism>! Stol band qilindi.</p>.
Ishora: «isSubmitting va isSubmitSuccessful» bo'limida aytdik — RHF takroriy yuborishni o'zi to'xtatmaydi. handleSubmit ning har chaqiruvi isSubmitting ni boshidan boshqaradi, shuning uchun tekshiruvni handleSubmit dan oldin qo'ying. Ismni handleSubmit bergan data dan oling.
Yechim
import { useState } from "react";
import { useForm } from "react-hook-form";
function wait(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export default function App() {
const [booked, setBooked] = useState(null);
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm({ defaultValues: { name: "", guests: "" } });
async function book(data) {
await wait(300);
console.log(`bron: ${data.name}, ${data.guests} kishi`);
setBooked(data.name);
}
function submit(event) {
if (isSubmitting) {
event.preventDefault(); // allaqachon yuborilmoqda
return;
}
void handleSubmit(book)(event);
}
return (
<form noValidate onSubmit={submit}>
<p>
<label htmlFor="name">Ism</label>
<input
id="name"
{...register("name", {
required: "Ismingizni yozing",
minLength: { value: 2, message: "Ism kamida 2 harf" },
})}
/>
</p>
{errors.name && <p role="alert">{errors.name.message}</p>}
<p>
<label htmlFor="guests">Mehmonlar soni</label>
<input
id="guests"
type="number"
{...register("guests", {
valueAsNumber: true,
required: "Nechta mehmon?",
min: { value: 1, message: "Kamida 1 kishi" },
max: { value: 12, message: "Ko'pi bilan 12 kishi" },
})}
/>
</p>
{errors.guests && <p role="alert">{errors.guests.message}</p>}
<button>
{isSubmitting ? "Yuborilmoqda…" : "Bron qilish"}
</button>
{booked && (
<p role="status">Rahmat, {booked}! Stol band qilindi.</p>
)}
</form>
);
}Konsolda:
bron: Malika, 4 kishiEkranda:
Ism
Mehmonlar soni
Bron qilish
Rahmat, Malika! Stol band qilindi.Tugma yuborish paytida yana bosildi (bos("Yuborilmoqda…")), lekin konsolda bitta qator va tugma matni "Yuborilmoqda…" da qoldi. Ikkinchi bosish paytida komponent "Yuborilmoqda…" holatida qayta chizilgan, yangi submit esa yangi isSubmitting ni (true) ko'radi va handleSubmit gacha bormaydi.
Nega tekshiruv book ichida emas? Biz sinadik: if (isSubmitting) return; ni book boshiga qo'ysak, ikkinchi book haqiqatan ham to'xtaydi. Lekin handleSubmit ning ikkinchi chaqiruvi tugashi bilan isSubmitting ni false qiladi — birinchi so'rov hali kutilayotgan paytda tugmada yana "Bron qilish" chiqadi. Tekshiruvni o'rovchidan oldin qo'yish — RHF holatiga umuman tegmaslik.
setBooked — oddiy useState. Ism data dan olindi: handleSubmit uni tayyor obyekt sifatida beradi.
4-mashq: Vazifalar qadami
feat/tahrir-formasi branch'ini oching, ikki paketni aniq versiya bilan o'rnating va marshrutlar.tsx ga tahrir bo'limini qo'shing. VazifaTahriri.tsx ni «Vazifalar qadami» bo'limidagi skelet bilan boshlang: useForm, register("matn"), noValidate, aria-invalid / aria-describedby, «Saqlash» tugmasi. Commit qilmang — fayl keyingi uch darsda to'ldiriladi.
Yechim
Tekshiruv ro'yxati:
git status—package.json,package-lock.json,marshrutlar.tsxo'zgargan,VazifaTahriri.tsxyangi.npm ls react-hook-form @hookform/resolvers—react-hook-form@7.89.0,@hookform/resolvers@5.9.1(^siz).npm run dev—/vazifa/1/tahrirochiladi, maydon va «Saqlash» bor. Sxema hali yo'q, shuning uchun tekshiruv ham yo'q.npm run tip—saqlahali yozilmagan bo'lsa, vaqtinchahandleSubmit(() => {})qo'ying; to'liq funksiya RHF + Zod darsida.
10. Real ishda
- Qayerda: ro'yxatdan o'tish, checkout (manzil, to'lov), admin panel formalari, CRM anketalari. RHF — React'dagi eng ko'p yuklanadigan forma kutubxonasi. shadcn/ui ning
Formkomponenti ham uning ustiga qurilgan (UI kutubxonalar xaritasi). - Xavfsizlik: brauzerdagi har tekshiruv — qulaylik, himoya emas. Hujumchi DevTools'da
noValidateni olib tashlashi yoki so'rovni formasiz, to'g'ridan yuborishi mumkin. Server baribir o'zi tekshiradi (Validatsiya serverda). - Testlar: RHF formasi Testing Library bilan oddiy foydalanuvchidek sinaladi: maydonga yozish, tugma bosish,
findByText("Ismingizni yozing")(RTL II). - Intervyu savollari: "controlled va uncontrolled forma farqi, RHF qaysi biri?", "
registernima qaytaradi?", "formStatenega Proxy?", "mode: onTouchedqanday ishlaydi?", "kichik formaga RHF kerakmi?".
Xulosa
- RHF — uncontrolled maydonlar ustidagi forma kutubxonasi: qiymat DOM'da, harf yozilganda render yo'q.
register("nom", qoidalar)→{ name, ref, onChange, onBlur },{...}bilan yoyiladi.handleSubmit(fn)— yig'adi, tekshiradi, keyinfn(data).- Qiymatlar satr: son uchun
valueAsNumber. Qoidalar:required,minLength,min,pattern,validate— xabar o'zbekcha, siz yozasiz. formState— Proxy: render paytida o'qilgani kuzatiladi.errors,isSubmitting,isDirty(boshlang'ich qiymatdan farq) —defaultValuesbilan.mode— xato qachon:onSubmit(sukut),onBlur,onTouched,onChange; keyinreValidateMode.- Kichik formaga React'ning
<form action>i yetadi; ishlab turgan formani ko'chirish — xavf va narx bilan o'lchanadigan qaror.
Keyingi dars: Zod chuqurroq: coerce, refine va transform — formadan keladigan "satr" qiymatlarni sxema bilan aylantirish, bir nechta maydonni birga tekshirish va o'zbekcha xabarlar; vazifalar formasining sxemasi.
Manbalar
- React Hook Form: Get Started,
useForm,register,handleSubmit,formState— react-hook-form.com/docs (2026-10 holatiga, v7.89.0) - React Hook Form: Advanced Usage (Accessibility,
aria-invalid) — react-hook-form.com/advanced-usage - React:
<form>,useActionState— react.dev/reference/react-dom/components/form - WAI-ARIA:
aria-invalid,aria-describedby— developer.mozilla.org/docs/Web/Accessibility/ARIA
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!