Никто не любит раз за разом создавать сложные формы с валидацией, и 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.
Читайте также:
- 6 актуальных советов по созданию чистого кода React
- Совместное использование компонентов React с различными библиотеками управления состоянием
- Что выбрать: React Native, Flutter или нативный подход
Читайте нас в Telegram, VK и Яндекс.Дзен
Перевод статьи Reed Barger: How to Build React Forms the Easy Way with react-hook-form