IlmHamroh
JavaScript Full-stack/19-qism. React: routing, server state, formalar29/36-dars29 daqiqa
Mundarija (35)

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'lsa fn(data) ni chaqiradi. Xatolar formState.errors da. Harf yozilganda komponent qayta chizilmaydi, shuning uchun katta forma ham tez ishlaydi.

Bu darsda

  • useForm, register va handleSubmit bilan forma yozasiz va register aslida nima qaytarishini ko'rasiz.
  • Tayyor qoidalar (required, minLength, min, pattern, validate) bilan o'zbekcha xato xabarlarini chiqarasiz.
  • formState dagi errors, isSubmitting, isDirty, isSubmitSuccessful ni ishlatasiz.
  • mode (onSubmit, onBlur, onChange, onTouched) — xato qachon ko'rinishini tanlaysiz.
  • Qachon RHF kerak, qachon React'ning o'z <form action> i yetarli — vazifalar dagi 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):

bash
npm install --save-exact react-hook-form@7.89.0

Saytdagi 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:

jsx
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:

text
(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:

jsx
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:

text
{name: 'Malika', guests: '4'}

handleSubmit(book) — "o'rovchi": u yangi funksiya qaytaradi va o'sha onSubmit ga beriladi. Forma yuborilganda u:

  1. event.preventDefault() ni chaqiradi — sahifa qayta yuklanmaydi;
  2. hamma ro'yxatdagi maydonlardan qiymatni yig'adi;
  3. qoidalarni tekshiradi (hozir qoida yo'q);
  4. 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:

jsx
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:

text
{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:

jsx
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:

text
render 1
render 2
render 3
render 4
render 5
render 6
render 7

Endi o'sha forma RHF bilan:

jsx
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:

text
render 1
render 2

Olti 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 obyektda value yo'q. Unda handleSubmit qiymatni 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:

jsx
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:

text
Ism
Ism kamida 2 harf
Telefon
Namuna: +998 90 000 00 00
Mehmonlar soni
Ko'pi bilan 12 kishi
Bron qilish

Har 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 holda type="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:

jsx
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:

text
phone

Ism 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:

jsx
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:

text
Ism
noDigits: Ismda raqam bo'lmasin
Bron qilish

validate 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: 12 qoidasi bor, mehmon maydonga "12" yozdi, lekin valueAsNumber yo'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:

jsx
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:

text
saqlandi: Jasur

Ekranda:

text
Ism
Bron qilish
Bron qabul qilindi

Tekshiruvchi 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:

jsx
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:

text
Izoh
isDirty: false
O'zgargan: yo'q

Yozilgach — 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: isDirty uchun defaultValues shart. Ularsiz RHF "boshlang'ich holat"ni bilmaydi va natija kutilmagan bo'lishi mumkin. Qoida: har formaga defaultValues bering — 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. isDirty nima?

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:

jsx
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:

text
Ism
Hammasi joyida

Qulaymi? 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:

jsx
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:

text
Ism
Telefon
Hammasi joyida
Keyingi maydon

Uch 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'zgarishda

Tekshirib 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:

  1. 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.
  2. Foyda yo'q. Bitta maydon, bitta xato. RHF ning kuchli tomonlari (ko'p maydon, isDirty, dinamik ro'yxat) bu yerda kerak emas.
  3. 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
bash
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):

diff
   "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):

diff
+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):

tsx
// 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 CHIZMAYDI

Bugun o'rganganimiz — formaning "skeleti". useForm dan olinadigan qismlar (kanon):

tsx
  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):

tsx
  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" (alert emas) — xato xushmuomala e'lon qilinadi, ekran o'quvchi gapini bo'lmaydi. Forma yuborilganda fokus baribir maydonga o'tadi, aria-describedby xatoni o'qitadi.
  • void handleSubmit(saqla)(event) — handleSubmit qaytargan funksiya Promise qaytaradi. ESLint'ning no-misused-promises qoidasi (ESLint II) onSubmit ga Promise qaytaradigan funksiyani bermaslikni talab qiladi. void — "natijasini ataylab kutmayman" degani.
  • register("matn") ga qoida yo'q. Qoidalar bu formada register da emas, sxemada bo'ladi — kelgusi darsda.

Yuborish tugmasi (kanon):

tsx
        {/* 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):

text
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 kB

Vite 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:

jsx
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:

text
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.guests ning turi (typeof)?
  • data.guests + 1 nima beradi?
  • Hech qanday mode berilmasa, birinchi tekshiruv qachon?
Yechim
  • 4 — onChange, onBlur, name, ref. value yo'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 uchun valueAsNumber: true.
  • onSubmit — sukut. Birinchi yuborishdan keyin reValidateMode: "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:

  1. name — majburiy: "Ismingizni yozing".
  2. rating — son (valueAsNumber), 1 dan 5 gacha: "Baho 1 dan 5 gacha".
  3. comment — ko'pi bilan 200 belgi: "Fikr 200 belgidan oshmasin".
  4. Har xato maydon ostida, role="alert" bilan; formada noValidate.

Ishora: «Tayyor qoidalar va xatolar» bo'limidagi bron formasi.

jsx
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:

text
{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
jsx
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):

text
Ism
Ismingizni yozing
Baho
Baho 1 dan 5 gacha
Fikr
Yuborish

To'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:

  1. name — majburiy, kamida 2 harf. guests — son, 1–12.
  2. book — async: 300 ms kutadi (wait), keyin konsolga bron: <ism>, <son> kishi.
  3. Yuborilayotganda tugma matni "Yuborilmoqda…", tugma o'chmaydi. Takroriy bosish book ni ikkinchi marta chaqirmasin.
  4. 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
jsx
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:

text
bron: Malika, 4 kishi

Ekranda:

text
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.tsx o'zgargan, VazifaTahriri.tsx yangi.
  • npm ls react-hook-form @hookform/resolvers — react-hook-form@7.89.0, @hookform/resolvers@5.9.1 (^ siz).
  • npm run dev — /vazifa/1/tahrir ochiladi, maydon va «Saqlash» bor. Sxema hali yo'q, shuning uchun tekshiruv ham yo'q.
  • npm run tip — saqla hali yozilmagan bo'lsa, vaqtincha handleSubmit(() => {}) 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 Form komponenti ham uning ustiga qurilgan (UI kutubxonalar xaritasi).
  • Xavfsizlik: brauzerdagi har tekshiruv — qulaylik, himoya emas. Hujumchi DevTools'da noValidate ni 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?", "register nima qaytaradi?", "formState nega Proxy?", "mode: onTouched qanday 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, keyin fn(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) — defaultValues bilan.
  • mode — xato qachon: onSubmit (sukut), onBlur, onTouched, onChange; keyin reValidateMode.
  • 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
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
React Hook Form: useForm, register, handleSubmit va xatolarni ko'rsatish — IlmHamroh