React Hook Form: создание сложных форм для начинающих

Привет, Хабр!
Сегодня рассмотрим важную тему для всех, кто занимается созданием сложных и многошаговых форм в React. Мы все знаем, как это бывает: бесконечные рендеры, тонны кода для валидации и управления состоянием, а также бесконечная борьба за оптимизацию производительности. Но никто уже давно не отчаивается, ведь существует мощное и гибкое решение — React Hook Form.
React Hook Form — это библиотека, которая использует концепцию неконтролируемых компонентов, чтобы минимизировать количество повторных рендеров и повысить производительность приложения.
Данная статья полезна для новичков, которые только начинают работать со сложными формами в React.
Создание сложных форм
Разделение формы на компоненты
Разделение формы на компоненты — это основа, которая позволяет создавать многошаговые формы. Каждый шаг формы может быть представлен отдельным компонентом:
// Step 1: PersonalInfo.js
import React from 'react';
import { useForm } from 'react-hook-form';
import { useHistory } from 'react-router-dom';
const PersonalInfo = ({ onSubmit }) => {
const { register, handleSubmit } = useForm();
const history = useHistory();
const handleNext = (data) => {
onSubmit(data);
history.push('/employment');
};
return (
);
};
export default PersonalInfo;
Контекст формы для управления состоянием данных
Контекст формы позволяетпередавать состояние формы между различными компонентами. Это можно сделать с помощью FormProvider
и useFormContext
:
// FormContext.js
import React, { createContext, useContext, useState } from 'react';
const FormContext = createContext();
export const useFormData = () => useContext(FormContext);
export const FormProvider = ({ children }) => {
const [formData, setFormData] = useState({});
const updateFormData = (data) => {
setFormData((prev) => ({ ...prev, ...data }));
};
return (
{children}
);
};
Пример создания шагов формы и их связка через React Router
Через React Router можно управлять навигацией между различными шагами формы:
// App.js
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { FormProvider } from './FormContext';
import PersonalInfo from './PersonalInfo';
import Employment from './Employment';
import Review from './Review';
const App = () => (
);
export default App;
// Step 2: Employment.js
import React from 'react';
import { useForm } from 'react-hook-form';
import { useHistory } from 'react-router-dom';
import { useFormData } from './FormContext';
const Employment = () => {
const { register, handleSubmit } = useForm();
const history = useHistory();
const { updateFormData } = useFormData();
const handleNext = (data) => {
updateFormData(data);
history.push('/review');
};
return (
);
};
export default Employment;
Подключение и управление состоянием формы с помощью хуков
Хуки useForm
, useFormContext
и другие позволяет управлять состоянием формы и валидировать данные:
// Step 3: Review.js
import React from 'react';
import { useFormData } from './FormContext';
const Review = () => {
const { formData } = useFormData();
const handleSubmit = () => {
console.log('Form submitted:', formData);
// Add submission logic here
};
return (
Review Your Information
{JSON.stringify(formData, null, 2)}
);
};
export default Review;
Валидация и обработка ошибок
Рассмотрим настройку встроенной валидации.
Можно настривать валидацию с помощью атрибутов required
, minLength
, maxLength
, pattern
, и validate
.
Пример:
import React from 'react';
import { useForm } from 'react-hook-form';
const SimpleForm = () => {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = (data) => {
console.log(data);
};
return (
);
};
export default SimpleForm;
Интеграция с библиотеками для схемной валидации
Для более хардовой валидации можно использовать библиотеки Yup и Zod. Эти библиотеки позволяют создавать схемы валидации и интегрировать их с React Hook Form.
Пример с Yup:
import React from 'react';
import { useForm, Controller } from 'react-hook-form';
import * as yup from 'yup';
import { yupResolver } from '@hookform/resolvers/yup';
const schema = yup.object().shape({
name: yup.string().required('Name is required'),
email: yup.string().email('Invalid email').required('Email is required'),
});
const YupForm = () => {
const { control, handleSubmit, formState: { errors } } = useForm({
resolver: yupResolver(schema),
});
const onSubmit = (data) => {
console.log(data);
};
return (
);
};
export default YupForm;
Обработка и отображение ошибок
Можно управлять отображением ошибок с помощью объекта errors
, предоставляемого хукем useForm
, пример:
import React from 'react';
import { useForm } from 'react-hook-form';
const ErrorHandlingForm = () => {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = (data) => {
console.log(data);
};
return (
);
};
export default ErrorHandlingForm;
Примеры для различных типов валидации
Пример валидации формы регистрации:
import React from 'react';
import { useForm } from 'react-hook-form';
const RegistrationForm = () => {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = (data) => {
console.log('Registration Data:', data);
};
return (
);
};
export default RegistrationForm;
Пример кастомной валидации пароля:
import React from 'react';
import { useForm } from 'react-hook-form';
const CustomValidationForm = () => {
const { register, handleSubmit, formState: { errors } } = useForm();
const validatePassword = (value) => {
if (value.length < 8) {
return 'Password must be at least 8 characters long';
} else if (!/[A-Z]/.test(value)) {
return 'Password must contain at least one uppercase letter';
} else if (!/[0-9]/.test(value)) {
return 'Password must contain at least one number';
}
return true;
};
const onSubmit = (data) => {
console.log('Form Data:', data);
};
return (
);
};
export default CustomValidationForm;
Прочие фичи
useFieldArray для динамического добавления/удаления полей формы
useFieldArray
— это мощный хук в React Hook Form, который позволяет динамически управлять массивом полей формы. Мастхев для создания форм, в которых пользователи могут добавлять или удалять элементы.
Создадим форму, в которой можно добавлять и удалять поля для ввода имен участников команды:
import React from 'react';
import { useForm, useFieldArray } from 'react-hook-form';
const TeamForm = () => {
const { register, control, handleSubmit } = useForm({
defaultValues: {
members: [{ name: '' }],
},
});
const { fields, append, remove } = useFieldArray({
control,
name: 'members',
});
const onSubmit = (data) => {
console.log(data);
};
return (
);
};
export default TeamForm;
Используем useForm
для управления состоянием формы и useFieldArray
для динамического управления массивом полей. Кнопки »Add Member» и »Remove» позволяют юзеру добавлять и удалять поля соответственно.
Оптимизация производительности форм
Когда есть формы с большим количеством полей, важно учитывать производительность, чтобы предотвратить задержки. И здесь у меня есть несколько советов:
Избегайте ненужных рендеров: используйте
React.memo
иuseMemo
, чтобы избежать повторных рендеров компонентов, которые не зависят от состояния формы.Контролируемые и неконтролируемые компоненты: React Hook Form использует неконтролируемые компоненты по дефолту, что снижает количество рендеров. Если нужно использовать контролируемые компоненты, убеждаемся, что мы оптимизируем их правильно.
Ленивая загрузка компонентов: загружаем компоненты формы по мере необходимости, а не все сразу.
Пример:
import React, { memo } from 'react';
import { useForm, Controller } from 'react-hook-form';
import { TextField, Button } from '@material-ui/core';
const LargeForm = () => {
const { control, handleSubmit } = useForm();
const onSubmit = (data) => {
console.log(data);
};
return (
);
};
export default memo(LargeForm);
Интеграция с внешними компонентами UI
React Hook Form легко интегрируется с внешними библиотеками компонентов UI, такими как Material-UI и Ant Design.
Пример кода с Material-UI:
import React from 'react';
import { useForm, Controller } from 'react-hook-form';
import { TextField, Button } from '@material-ui/core';
const MaterialUIForm = () => {
const { control, handleSubmit } = useForm();
const onSubmit = (data) => {
console.log(data);
};
return (
);
};
export default MaterialUIForm;
С React Hook Form создание сложных форм в React становится значительно проще!
Все актуальные методы и инструменты программирования можно освоить на онлайн-курсах OTUS: в каталоге можно посмотреть список всех программ, а в календаре — записаться на открытые уроки.