Урок 07

React Hook Form

React Hook Form — популярная библиотека для форм. Меньше ререндеров, встроенная валидация, удобный API.

Установка

npm install react-hook-form

Базовый пример

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

type FormValues = {
    email: string;
    password: string;
};

function LoginForm() {
    const { register, handleSubmit, formState: { errors } } = useForm<FormValues>();

    function onSubmit(data: FormValues) {
        console.log(data);
    }

    return (
        <form onSubmit={handleSubmit(onSubmit)}>
            <input {...register('email')} />
            {errors.email && <p>{errors.email.message}</p>}

            <input type="password" {...register('password')} />
            {errors.password && <p>{errors.password.message}</p>}

            <button>Войти</button>
        </form>
    );
}

register регистрирует поле. handleSubmit оборачивает отправку. formState.errors содержит ошибки.

Валидация

<input
    {...register('email', {
        required: 'Email обязателен',
        pattern: {
            value: /^\S+@\S+\.\S+$/,
            message: 'Некорректный email',
        },
    })}
/>

<input
    type="password"
    {...register('password', {
        required: 'Пароль обязателен',
        minLength: { value: 8, message: 'Минимум 8 символов' },
    })}
/>

Правила:

  • required
  • min, max
  • minLength, maxLength
  • pattern
  • validate — кастомная функция

Кастомная валидация

<input
    {...register('username', {
        validate: (value) =>
            value !== 'admin' || 'Имя admin занято',
    })}
/>

// Или функция
validate: (value) => {
    if (value.length < 3) return 'Слишком короткое';
    if (!/^[a-z]+$/.test(value)) return 'Только строчные буквы';
    return true;
}

Значения по умолчанию

useForm<FormValues>({
    defaultValues: {
        email: '',
        password: '',
    },
});

Состояние формы

const {
    formState: { errors, isSubmitting, isDirty, isValid },
} = useForm<FormValues>();
  • errors — объект ошибок
  • isSubmitting — идёт отправка
  • isDirty — были изменения
  • isValid — форма валидна

Сброс

const { reset } = useForm<FormValues>();

reset();               // к defaultValues
reset({ email: '' });  // к указанным

Watch

Следить за значением:

const { watch } = useForm<FormValues>();
const email = watch('email');
const allValues = watch();

SetValue и getValues

const { setValue, getValues } = useForm<FormValues>();

setValue('email', 'test@example.com');
const current = getValues('email');

Controller для сторонних компонентов

Если компонент не поддерживает ref (например, UI-библиотека):

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

<Controller
    name="birthday"
    control={control}
    render={({ field }) => (
        <DatePicker {...field} />
    )}
/>

Отправка на сервер

async function onSubmit(data: FormValues) {
    const res = await fetch('/api/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data),
    });

    if (!res.ok) {
        const errors = await res.json();
        // setError('email', { message: errors.email });
    }
}

Почему быстрее

React Hook Form использует неконтролируемые поля. Значения читаются при отправке, а не на каждый ввод. Это уменьшает количество ререндеров.

Итоги

  • React Hook Form уменьшает ререндеры
  • register, handleSubmit, formState
  • Встроенная валидация
  • Controller для сторонних компонентов
  • watch, setValue, reset для управления
  • Стандарт для сложных форм