Никто не любит раз за разом создавать сложные формы с валидацией, и React-разработчики  —  не исключение.

Когда дело доходит до создания форм в React, важно выбрать такую библиотеку, которая предоставляет максимум удобных инструментов и требует минимум кода.

Исходя из этих двух критериев  —  полезности и простоты  —  идеальным вариантом будет react-hook-form.

Посмотрим, как применить react-hook-form в проектах для создания насыщенных, функциональных форм.

Установка react-hook-form

Рассмотрим типичный случай использования: пользователь регистрируется в приложении.

У формы регистрации будет три поля ввода: для имени нового пользователя, его пароля и электронной почты. 

import React from "react";

const styles = {
container: {
width: "80%",
margin: "0 auto",
},
input: {
width: "100%",
},
};

export default function Signup() {
return (
<div style={styles.container}>
<h4>Sign up</h4>
<form>
<input placeholder="Username" style={styles.input} />
<input placeholder="Email" style={styles.input} />
<input placeholder="Password" style={styles.input} />
<button type="submit">Submit</button>
</form>
</div>
);
}

Как только проект React запустится, сразу же установим библиотеку react-hook-form.

npm i react-hook-form

Как пользоваться хуком useForm

Чтобы начать работу с react-hook-form, нужно просто вызвать хук useForm.

Когда мы это сделаем, то получим объект, из которого деструктурируем свойство register.

register  —  это функция, которую нужно подключить к каждому из полей ввода в качестве ссылки.

function App() {
const { register } = useForm();

return (
<div style={styles.container}>
<h4>Signup</h4>
<form>
<input ref={register} placeholder="Username" style={styles.input} />
<input ref={register} placeholder="Email" style={styles.input} />
<input ref={register} placeholder="Password" style={styles.input} />
<button type="submit">Submit</button>
</form>
</div>
);
}

Функция register будет принимать значение, которое пользователь ввел в каждое поле, и проверять его. register также передаст каждое значение в функцию, которая будет вызвана при отправке формы. Эту функцию мы рассмотрим далее.

Чтобы регистрация прошла успешно, для каждого поля ввода нужно указать соответствующий атрибут имени. Например, для поля ввода имени пользователя это будет атрибут “username”.

Это необходимо потому, что при отправке формы мы получим все значения входных данных для объекта. Каждое из свойств объекта будет названо в соответствии с указанными атрибутами имени для полей ввода.

function App() {
const { register } = useForm();

return (
<div style={styles.container}>
<h4>My Form</h4>
<form>
<input
name="username"
ref={register}
placeholder="Username"
style={styles.input}
/>
<input
name="email"
ref={register}
placeholder="Email"
style={styles.input}
/>
<input
name="password"
ref={register}
placeholder="Password"
style={styles.input}
/>
<button type="submit">Submit</button>
</form>
</div>
);
}

Как отправить форму с помощью handleSubmit

Чтобы обработать отправку формы и получение входных данных, добавим onSubmit в элемент формы и подключим к локальной функции с тем же именем.

function App() {
const { register } = useForm();

function onSubmit() {}

return (
<div style={styles.container}>
<h4>My Form</h4>
<form onSubmit={onSubmit}>
<input
name="username"
ref={register}
placeholder="Username"
style={styles.input}
/>
<input
name="email"
ref={register}
placeholder="Email"
style={styles.input}
/>
<input
name="password"
ref={register}
placeholder="Password"
style={styles.input}
/>
<button type="submit">Submit</button>
</form>
</div>
);
}

Из useForm возьмем функцию handleSubmit и обернем ее вокруг onSubmit как функцию более высокого порядка.

Функция handleSubmit позаботится о сборе всех данных из полей ввода —  мы получим их в onSubmit в виде объекта data.

Теперь по команде console.log(data) мы можем увидеть, что было введено в каждое из полей, через свойство с тем же именем.

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

function onSubmit(data) {
console.log(data);
// { username: 'test', email: 'test', password: 'test' }
}

return (
<div style={styles.container}>
<h4>Signup</h4>
<form onSubmit={handleSubmit(onSubmit)}>
<input
name="username"
ref={register}
placeholder="Username"
style={styles.input}
/>
<input
name="email"
ref={register}
placeholder="Email"
style={styles.input}
/>
<input
name="password"
ref={register}
placeholder="Password"
style={styles.input}
/>
<button type="submit">Submit</button>
</form>
</div>
);
}

Параметры валидации с помощью функции register

Проверить форму и добавить ограничения для каждого значения на вводе очень просто  —  нужно просто передать информацию в функцию register.

register принимает объект, включающий ряд свойств, которые сообщат register, как проверить предоставленные на вводе данные.

Первое свойство будет required. По умолчанию оно установлено в значение false, но мы можем изменить значение на true, чтобы убедиться, что форма не будет отправлена, пока не заполнены те или иные поля.

Мы хотим, чтобы значение username было обязательным, а имена пользователей были длиннее 6 символов, но короче 24.

Чтобы применить эту проверку, установим ограничение minLength равным 6, а maxLength  —  24.

<input
name="username"
ref={register({
required: true,
minLength: 6,
maxLength: 24,
})}
style={styles.input}
placeholder="Username"
/>

При вводе чисел (допустим, форма ввода отвечает за возраст пользователя) мы бы воспользовались свойствами min и max вместо minLength и maxLength.

Затем при желании можно добавить в проверку шаблон регулярного выражения.

Чтобы имя пользователя содержало только прописные и строчные символы, в валидации нам поможет следующее регулярное выражение:

<input
name="username"
ref={register({
required: true,
minLength: 6,
maxLength: 20,
pattern: /^[A-Za-z]+$/i,
})}
style={styles.input}
placeholder="Username"
/>

И, наконец, есть validate, кастомная функция, которая открывает доступ к значению, введенному в поле. Она позволяет предоставить собственную логику, которая определит, допустимо ли введенное значение (возвращаться будет логическое значение true или false).

Поле ввода электронной почты также должно быть обязательным и содержать действительные данные. Чтобы проверить это, мы можем передать входные данные в функцию из валидатора библиотеки под названием isEmail.

Если на вводе действительно адрес электронной почты, то возвращается значение true. В противном случае  —  false.

<input
name="email"
ref={register({
required: true,
validate: (input) => isEmail(input), // Возвращает true, если ввод корректный
})}
style={styles.input}
placeholder="Email"
/>

Для функции register, отвечающей за пароль, мы установим required в значение true, minLength равным 6, а maxLength устанавливать не будем.

<input
name="password"
ref={register({
required: true,
minLength: 6
})}
style={styles.input}
placeholder="Password"
/>

Как отобразить ошибки

На данном этапе форма не сообщает пользователю, что что-то пошло не так. Нам нужно дать обратную связь по введенным значениям.

Когда содержимое какого-то из полей ввода недействительно, данные формы просто не передаются (onSubmit не вызывается). Кроме того, первый ввод с ошибкой автоматически остается в фокусе, что не дает пользователю никакой обратной связи о том, что произошло.

Вместо того, чтобы просто не отправлять форму, мы можем захватить объект ошибок errors из useForm.

И точно так же, как с функцией data, которую мы получаем в onSubmit, errors содержит свойства, соответствующие именам каждого из полей ввода, если там содержится ошибка.

В данном примере мы можем добавить условие к каждому полю ввода и указать, что если ошибка есть, то цвет borderColor меняется на красный.

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

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

  return (
    <div style={styles.container}>
      <h4>My Form</h4>
      <form onSubmit={handleSubmit(onSubmit)}>
        <input
          name="username"
          ref={register({
            required: true,
            minLength: 6,
            maxLength: 20,
            pattern: /^[A-Za-z]+$/i,
          })}
          style={{ ...styles.input, borderColor: errors.username && "red" }}
          placeholder="Username"
        />
        <input
          name="email"
          ref={register({
            required: true,
            validate: (input) => isEmail(input),
          })}
          style={{ ...styles.input, borderColor: errors.email && "red" }}
          placeholder="Email"
        />
        <input
          name="password"
          ref={register({
            required: true,
            minLength: 6,
          })}
          style={{ ...styles.input, borderColor: errors.password && "red" }}
          placeholder="Password"
        />
        <button type="submit" disabled={formState.isSubmitting}>
          Submit
        </button>
      </form>
    </div>
  );
}

Режим проверки

По умолчанию объект errors обновляется при отправке формы. Валидация, таким образом, выполняется только в этом случае.

Мы можем изменить это, передав useForm объект, в котором установлен желательный режим проверки: onBlur, onChange или onSubmit.

onBlur будет выполнять проверку всякий раз, когда пользователь нажимает вне поля ввода. onChange  —  всякий раз, когда введенное пользователем значение изменяется, а onSubmit  —  всякий раз, когда отправляется форма.

Для нашей формы выберем onBlur.

const { register, handleSubmit, errors } = useForm({
mode: "onBlur",
});

Обратите внимание: есть и другие помощники для ручной установки и очистки ошибок (setError и clearError). Ими можно воспользоваться, например, когда надо создать другую ошибку или самостоятельно очистить ошибку в onSubmit.

Как отключить форму с помощью formState

Последнее значение, которое мы можем получить с помощью хука useForm,  —  это formState.

Он сообщает, к примеру, о том, когда произошло какое-то действие с данными в полях ввода или когда была отправлена форма.

Поэтому, если вам надо на время отключить кнопку формы, чтобы форма не отправлялась большее количество раз, чем необходимо, установите значение disabled для formState.isSubmitting.

Всякий раз, когда мы отправляем форму, она будет отключаться  —  до тех пор, пока не выполнится проверка и не запустится функция onSubmit.

Заключение

Надеемся, что эта статья показала вам, как легче создавать функциональные формы в приложениях React.

У react-hook-form есть еще много функций, которые здесь не рассмотрены. Однако в документации можно найти любой вариант использования, который придет вам в голову.

Если вам интересно увидеть окончательную версию кода из статьи, переходите по ссылке на CodeSandbox.

Читайте также:

Читайте нас в Telegram, VK и Яндекс.Дзен


Перевод статьи Reed Barger: How to Build React Forms the Easy Way with react-hook-form

Предыдущая статьяИнтуитивная основа обучения с подкреплением
Следующая статьяPython Django: как изменить страницу ошибки 404?