React Hook Form — o'rnatish, register, handleSubmit

~20 daq · React · Formalar

Muammodan boshlaylik

Ro'yxatdan o'tish formasi: 12 ta maydon. 62-darsdagi controlled usul bilan:

  • Bitta katta state obyekti va handleChange
  • Har bir maydon uchun xato xabari — yana bir state obyekti
  • "Maydonga tegildimi" (touched) — yana bitta
  • Yuborilmoqda holati
  • Va asosiysi: har bir harfda butun forma qayta render bo'ladi. 12 ta maydon, murakkab komponentlar — telefonda yozish sezilarli sekinlashadi

Bu takroriy kod har bir formada yoziladi. React Hook Form (RHF) — bu muammolarni hal qiluvchi eng mashhur forma kutubxonasi.


O'rnatish

npm install react-hook-form

Hajmi ~10 KB, qo'shimcha bog'liqliklarsiz.


Birinchi forma

import { useForm } from 'react-hook-form';

function Login() {
  const { register, handleSubmit } = useForm();

  function onSubmit(data) {
    console.log(data);   // { email: 'ali@mail.uz', parol: '12345678' }
  }

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label htmlFor="email">Email</label>
      <input id="email" type="email" {...register('email')} />

      <label htmlFor="parol">Parol</label>
      <input id="parol" type="password" {...register('parol')} />

      <button type="submit">Kirish</button>
    </form>
  );
}

useState yo'q, onChange yo'q, value yo'q, preventDefault yo'q.


register — maydonni ro'yxatga olish

<input {...register('email')} />

register('email') obyekt qaytaradi:

{
  name: 'email',
  onChange: ...,     // qiymat o'zgarganini RHF ga xabar beradi
  onBlur: ...,       // maydondan chiqilganini
  ref: ...,          // DOM elementga havola
}

{...} spread bilan bu xususiyatlar input ga qo'yiladi. RHF qiymatni DOM dan ref orqali o'qiydi — React state da saqlamaydi.

Ichma-ich nomlar

<input {...register('ism')} />
<input {...register('manzil.shahar')} />
<input {...register('manzil.kocha')} />
<input {...register('telefonlar.0')} />
// natija
{
  ism: 'Ali',
  manzil: { shahar: 'Toshkent', kocha: 'Amir Temur' },
  telefonlar: ['+998901234567'],
}

Nuqta — ichma-ich obyekt, raqam — massiv indeksi.

Turli input turlari

<input type="checkbox" {...register('shartlar')} />              // true / false

<input type="checkbox" value="html" {...register('kurslar')} />  // bir xil nom →
<input type="checkbox" value="css" {...register('kurslar')} />   // ['html', 'css']

<input type="radio" value="kuryer" {...register('yetkazish')} />
<input type="radio" value="pochta" {...register('yetkazish')} />

<select {...register('viloyat')}>
  <option value="toshkent">Toshkent</option>
  <option value="samarqand">Samarqand</option>
</select>

<textarea {...register('izoh')} />

<input type="number" {...register('yosh', { valueAsNumber: true })} />  // son
<input type="date" {...register('sana', { valueAsDate: true })} />      // Date

[!INFO] valueAsNumber: true bo'lmasa, number input ning qiymati string bo'ladi (62-dars). Bo'sh maydon valueAsNumber bilan NaN beradi — buni validatsiyada hisobga oling (64-dars).


handleSubmit

<form onSubmit={handleSubmit(onSubmit, onError)}>

handleSubmit:

  1. e.preventDefault() ni o'zi chaqiradi
  2. Barcha maydonlarni validatsiya qiladi (64-dars)
  3. Hammasi to'g'ri bo'lsa — onSubmit(data) ni chaqiradi
  4. Xato bo'lsa — onError(errors) ni (ixtiyoriy) chaqiradi va birinchi xatoli maydonga fokus qiladi

onSubmit async bo'lishi mumkin — RHF uni kutadi.


defaultValues

const { register, handleSubmit } = useForm({
  defaultValues: {
    ism: '',
    email: '',
    viloyat: 'toshkent',
    shartlar: false,
    kurslar: [],
  },
});

Boshlang'ich qiymatlarni shu yerda bering — input dagi defaultValue da emas. Bu reset() qaysi qiymatlarga qaytishini va isDirty (o'zgartirilganmi) ni to'g'ri hisoblash uchun muhim.

Tahrirlash formasi — serverdan ma'lumot

function ProfilTahrir() {
  const { data: profil } = useProfil();          // TanStack Query
  const { register, handleSubmit, reset } = useForm();

  useEffect(() => {
    if (profil) reset(profil);                   // kelganda formani to'ldirish
  }, [profil, reset]);

  // ...
}

Yoki values parametri bilan — qiymat o'zgarsa forma avtomatik yangilanadi:

const { register } = useForm({ values: profil });

formState — forma holati

const {
  register,
  handleSubmit,
  formState: { isSubmitting, isDirty, isValid, errors, isSubmitSuccessful },
} = useForm();
Qiymat Ma'nosi
errors Har bir maydonning xatolari (64-dars)
isSubmitting onSubmit bajarilmoqda (async bo'lsa — tugaguncha)
isSubmitSuccessful Muvaffaqiyatli yuborildi
isDirty Biror maydon defaultValues dan farq qiladi
dirtyFields Qaysi maydonlar o'zgargan
touchedFields Qaysi maydonlarga tegilgan (blur)
isValid Butun forma to'g'rimi
async function onSubmit(data) {
  await fetch('/api/royxat', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(data),
  });
}

<button type="submit" disabled={isSubmitting}>
  {isSubmitting ? 'Yuborilmoqda...' : "Ro'yxatdan o'tish"}
</button>

isSubmitting uchun alohida useState kerak emas — onSubmit dan qaytgan Promise tugaguncha true.

[!WARNING] formState — Proxy: RHF faqat siz o'qigan maydonlarni kuzatadi va faqat ular o'zgarganda render qiladi. Shuning uchun formState dan kerakli qiymatlarni render paytida destructure qiling: const { errors, isSubmitting } = formState. Uni useEffect ichida yoki shartli ravishda o'qisangiz, yangilanishlarni o'tkazib yuborishingiz mumkin.


Boshqa foydali funksiyalar

const { watch, setValue, getValues, reset, setFocus } = useForm();

// watch — qiymatni kuzatish (o'zgarganda render)
const yetkazish = watch('yetkazish');
{yetkazish === 'kuryer' && <input {...register('manzil')} />}

// setValue — dasturiy o'zgartirish
setValue('shahar', 'Toshkent', { shouldValidate: true, shouldDirty: true });

// getValues — renderSiz o'qish
const email = getValues('email');

// reset — boshlang'ich holatga yoki yangi qiymatlarga
reset();
reset({ email: '' });

// setFocus
setFocus('email');

[!TIP] watch komponentni har o'zgarishda qayta render qiladi. Faqat bitta maydonni kuzatadigan kichik bola komponentda useWatch({ control, name: 'yetkazish' }) ishlating — shunda faqat o'sha bola render bo'ladi, butun forma emas.


Nima uchun shunday?

Nima uchun React Hook Form qiymatlarni React state da emas, DOM da saqlaydi — bu React ning "controlled" falsafasiga zid emasmi?

Bu ongli tanlov, va u tezlik uchun. Controlled formada har bir harf setState → butun forma komponenti va uning barcha bolalari qayta render. 30 maydonli formada bu har harfda 30 ta komponentni tekshirish degani. RHF esa input larni uncontrolled qoldiradi (29-dars): brauzer qiymatni o'zi saqlaydi, RHF unga ref orqali kerak bo'lganda qaraydi va faqat muhim narsalar (xato paydo bo'ldi, forma yuborildi) o'zgarganda render qiladi. Natijada katta formalarda ham har bir harf — nol React render. Controlled kerak bo'lgan joyda esa (murakkab UI kutubxonalari komponentlari) RHF Controller ni taklif qiladi (66-dars).


Xulosa

Eslab qoling:

  • useForm() → register, handleSubmit, formState, reset, watch, setValue
  • {...register('nom')} — maydonni ulaydi: name, onChange, onBlur, ref
  • Ichma-ich nomlar: 'manzil.shahar', 'telefonlar.0'
  • valueAsNumber, valueAsDate — tur o'zgartirish
  • handleSubmit(onSubmit) — preventDefault, validatsiya, birinchi xatoga fokus
  • defaultValues — boshlang'ich qiymatlar; serverdan — reset(data) yoki values
  • formState: isSubmitting, isDirty, isValid, errors — render paytida destructure qiling
  • Qiymatlar DOM da — har harfda render yo'q

Keyingi dars

64-dars: React Hook Form — validation, error xabarlar