Модуль 29

Робота з формами: React Hook Form

Ось твій урок у стилі CS50. Вмикай енергію на максимум, ми починаємо! 🚀


🎓 Урок: Робота з формами та магія React Hook Form

Привіт, друзі! Це CS50 (умовно 😉), і сьогодні ми поговоримо про те, що є на 99% веб-сайтів.

Це те, через що ми реєструємося, купуємо квитки, пишемо коментарі та відправляємо гроші. Так, це ФОРМИ.

Здавалося б, ну що там складного? <input>, <button>, поїхали. Але якщо ви хоч раз намагалися написати велику форму на "чистому" React, ви знаєте цей біль.


1. 🔥 Вступ: Чому старий спосіб — це пекло?

Уявіть ситуацію. Ви будуєте сторінку оформлення замовлення ("Checkout"). Там є: Ім’я, Прізвище, Адреса, Місто, Індекс, Номер картки... скажімо, 15 полів.

Як ми робили це раніше? Ми створювали 15 станів (useState), по одному на кожне поле. Або один велетенський об’єкт стану. І щоразу, коли користувач натискає одну літеру, React перемальовує (робить ре-рендер) весь компонент.

🤯 Риторичне питання: Чи має сенс перемальовувати всю "кухню", якщо ви просто поклали одну ложку на стіл? Звісно, ні!

А тепер додайте сюди валідацію. "Пароль має бути не менше 8 символів", "Email невалідний". Вам доведеться писати купу функцій if (password.length < 8) .... Це перетворює ваш код на "спагеті".

Аналогія: Уявіть, що ви заповнюєте паперову анкету в банку. * Стандартний підхід React (Controlled Inputs): Банкір стоїть у вас над душею і вихоплює анкету після кожної написаної літери, щоб перевірити її, а потім віддає назад. Це дратує і це повільно. * React Hook Form (Uncontrolled Inputs): Банкір дає вам анкету і каже: "Заповни, як закінчиш — поклич, я перевірю все разом". Це швидко, ефективно і логічно.

Сьогодні ми навчимося бути "розумним банкіром" за допомогою бібліотеки React Hook Form.


2. 🧠 Теоретична база (Що під капотом?)

React Hook Form (RHF) працює інакше, ніж стандартні форми в React.

Ключова відмінність:

Замість того, щоб React контролював кожне натискання клавіші (Controlled Components), RHF дозволяє браузеру зберігати значення інпуту, а React "підглядає" туди тільки коли треба (Uncontrolled Components).

Головні інструменти (Ваш арсенал):

  1. useForm — це головний хук. Він як менеджер, що керує всім процесом.
  2. register — це магічна функція. Вона "реєструє" ваш інпут у системі. Це як видати бейджик співробітнику: "Тепер ти працюєш на мене, я слідкую за тобою".
  3. handleSubmit — охоронець на вході. Він не дозволить формі відправитися, доки всі дані не будуть валідними.
  4. formState: { errors } — об'єкт, де живуть усі помилки. Якщо користувач накосячив, RHF покладе туди записку з поясненням.

☝️ Що треба зрозуміти інтуїтивно:

Ви не створюєте useState для кожного поля. Ви просто кажете RHF: "Ось інпут, ось його правила (наприклад, обов'язковий). Слідкуй за ним сам".


3. 🧪 Приклади: Від "Hello World" до Реальності

Приклад 1: Мінімальна база

Що ми очікуємо? Просту форму з одним полем, яка виводить дані в консоль.

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

export default function SimpleForm() {
  // 1. Ініціалізуємо хук
  const { register, handleSubmit } = useForm();

  // 2. Функція, яка спрацює ТІЛЬКИ якщо все ок
  const onSubmit = (data) => console.log("Дані форми:", data);

  return (
    // 3. handleSubmit обгортає нашу функцію
    <form onSubmit={handleSubmit(onSubmit)}>

      {/* 4. Реєструємо інпут під іменем "firstName" */}
      <input {...register("firstName")} placeholder="Ваше ім'я" />

      <button type="submit">Надіслати</button>
    </form>
  );
}

🔍 Розбір: Бачите ...register("firstName")? Цей спред-синтаксис (...) автоматично додає інпуту name, onChange, onBlur і ref. Ми не писали жодного useState!


Приклад 2: Додаємо правила (Валідація)

У реальному житті не можна просто дозволити відправити порожнє поле. Давайте додамо вимогу: ім'я обов'язкове.

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

export default function ValidationForm() {
  const { 
    register, 
    handleSubmit, 
    formState: { errors } // Дістаємо об'єкт помилок
  } = useForm();

  const onSubmit = (data) => alert(`Привіт, ${data.username}!`);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>Username:</label>

      {/* Додаємо об'єкт з правилами другим аргументом */}
      <input 
        {...register("username", { required: "Це поле обов'язкове!" })} 
        style={{ borderColor: errors.username ? 'red' : 'black' }}
      />

      {/* Якщо є помилка в username, показуємо її текст */}
      {errors.username && <p style={{color: 'red'}}>{errors.username.message}</p>}

      <button type="submit">Увійти</button>
    </form>
  );
}

Чому це круто? RHF сам перевіряє інпут. Якщо ви натиснете "Увійти" з порожнім полем, onSubmit навіть не запуститься. Фокус автоматично перестрибне на поле з помилкою. Зручно, правда?


4. 🛠 Практична частина

Час забруднити руки кодом! Відкривайте редактор.

🔹 Завдання 1: Реплікація Створіть компонент LoginForm, який має два поля: email та password. Виведіть дані в console.log при сабміті.

🔹 Завдання 2: Валідація Email Додайте до поля email валідацію. * Воно має бути required. * Воно має відповідати патерну email (підказка: в register є властивість pattern).

🔹 Завдання 3: "Секретний пароль" Зробіть так, щоб пароль мав мінімальну довжину 6 символів (minLength). Якщо менше — покажіть текст "Занадто короткий пароль!".

🔹 Завдання 4: Міні-кейс Створіть чекбокс "Я погоджуюсь з умовами". Кнопка "Реєстрація" має працювати, тільки якщо чекбокс відмічено (це теж робиться через register і required).

🔹 Питання "А що, якщо...": Що станеться, якщо у двох інпутів в register буде однакове ім'я (наприклад, ...register("user"))? Спробуйте і подивіться, яке значення переможе.


5. 💡 Мислення як у розробника

Як відрізнити новачка від профі, дивлячись на код форми?

1. Новачок: Намагається контролювати кожен чих. onChange={(e) => setName(e.target.value)}. Він бореться з React, замість того щоб використовувати DOM.

2. Досвідчений розробник: Думає про продуктивність (менше ре-рендерів) і DX (Developer Experience — зручність написання коду). Він використовує React Hook Form, тому що це зменшує код на 50% і робить його чистішим.

⚠️ Типова помилка: Забувати деструктуризувати register або handleSubmit. Або плутати ім'я поля в register("email") і те, що приходить в onSubmit (там теж буде об'єкт { email: ... }).

Порада: Завжди дивіться в console.log(errors), коли щось не працює. RHF дуже чітко каже, де проблема.


6. 🧩 Підсумок

Отже, що ми сьогодні зробили?

  1. Зрозуміли, що контролювати кожен інпут вручну — це минуле століття.
  2. Познайомилися з useForm та register — нашими новими найкращими друзями.
  3. Навчилися робити валідацію без головного болю та купу if/else.
  4. Зробили наші додатки швидшими, бо позбулися зайвих рендерів.

Ви тепер вмієте: Створювати форми будь-якої складності, які не "гальмують" і самі себе перевіряють.

🔜 Тизер наступного уроку: А що, якщо у вас форма на 50 полів і валідація дуже складна (наприклад, "пароль має містити цифру, літеру і смайлик")? Писати правила в register стане незручно. На наступному уроці ми підключимо "важку артилерію" — бібліотеку схем Zod або Yup, яка зробить вашу валідацію куленепробивною.

А поки що — це був CS50! (тобто наш урок 😉). Кодьте із задоволенням!