diff --git a/pages/lesson-2/chapter-1.ru.mdx b/pages/lesson-2/chapter-1.ru.mdx index 806d3d8..bfaf75c 100644 --- a/pages/lesson-2/chapter-1.ru.mdx +++ b/pages/lesson-2/chapter-1.ru.mdx @@ -19,7 +19,7 @@ import { Button } from '../../ui/button' import { TextField } from '../../ui/text-field' type FormValues = { - login: string + email: string password: string } @@ -32,7 +32,7 @@ export const LoginForm = () => { return (
- + @@ -64,14 +64,14 @@ export const Primary: Story = {} ```tsx filename="login-form.tsx" showLineNumbers {4,11} type FormValues = { - login: string + email: string password: string rememberMe: boolean } ... return (
- + @@ -94,7 +94,7 @@ import { TextField } from '../../ui/text-field' import { Button } from '../../ui/button' type FormValues = { - login: string + email: string password: string rememberMe: boolean } @@ -116,7 +116,7 @@ export const LoginForm = () => { return ( - + diff --git a/pages/lesson-2/chapter-2.ru.mdx b/pages/lesson-2/chapter-2.ru.mdx index 922266a..2a2c2b4 100644 --- a/pages/lesson-2/chapter-2.ru.mdx +++ b/pages/lesson-2/chapter-2.ru.mdx @@ -1 +1,166 @@ -# Under construction +import { Callout } from 'nextra/components' + +## Нативная валидация через react-hook-form + +React-hook-form поддерживает валидации [из коробки](https://react-hook-form.com/get-started#Applyvalidation) + +Давайте провалидируем нашу форму: + +- Email должен быть обязательным полем и валидным email адресом +- Пароль должен быть обязательным полем и иметь минимум 3 символа + +```tsx filename="src/components/auth/login-form/login-form.stories.tsx" {3,4,5,6,10,11,12,13} showLineNumbers + + + + + + +... + +const emailRegex = + /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/ +``` + +Добавим логирование ошибок что бы проверить что валидация работает правильно: + +```tsx filename="src/components/auth/login-form/login-form.stories.tsx" {5,8} showLineNumbers +const { + control, + register, + handleSubmit, + formState: { errors }, +} = useForm() + +console.log('errors: ', errors) +``` + +Получим следующий результат: + +![react-hook-form validation](./images/login-form-native-validation.png) + +Теперь покажем эти ошибки пользователю: + +```tsx filename="src/components/auth/login-form/login-form.tsx" {8,16} showLineNumbers +
+ + + + + +``` + +Получаем следующий результат: +![login-form-native-validation-show-errors](./images/login-form-native-validation-show-errors.png) + +## Валидация с помощью библиотек + +Такое решение отлично подходит для форм с несложными валидациями и небольшим количеством полей. Но если у нас будет форма с 10 полями и сложными валидациями, то код будет нечитаемым и сложным для поддержки. + +Для таких случаев есть библиотеки, которые помогают упростить валидацию форм. + +- [Zod](https://zod.dev) +- [Joi](https://github.com/hapijs/joi) +- [Yup](https://github.com/jquense/yup) + +Все они поддерживаются react-hook-form с помощью [@hookform/resolvers](https://www.npmjs.com/package/@hookform/resolvers) + +Все они действуют по одному принципу, но лично мне больше всего нравится Zod, поэтому его и будем использовать. + +### Zod + +Установим Zod и @hookform/resolvers + +```bash filename="Terminal" +pnpm i zod @hookform/resolvers +``` + +Создадим валидационную схему: + +```tsx filename="src/components/auth/login-form/login-form.tsx" showLineNumbers +import { z } from 'zod' + +const loginSchema = z.object({ + email: z.string().email(), + password: z.string().min(3), +}) +``` + +Используем ее в useForm: + +```tsx filename="src/components/auth/login-form/login-form.tsx" showLineNumbers {1,8,9,10} +import { zodResolver } from '@hookform/resolvers/zod' + +const { + control, + register, + handleSubmit, + formState: { errors }, +} = useForm({ + resolver: zodResolver(loginSchema), +}) +``` + +Удалим валидацию из TextField: + +```tsx filename="src/components/auth/login-form/login-form.tsx" {2,3} showLineNumbers +
+ + + + + +``` + +Получим следующий результат: + +![login-form-zod-validation-basic](./images/login-form-zod-validation-basic.png) + +Сообщения об ошибках создались автоматически, но мы можем и поменять их на свои, подробнее можно узнать в [документации](https://zod.dev/?id=strings) + +### Автогенерация типов для форм + +Для того что бы не писать типы для формы вручную, можно воспользоваться `z.infer`: + +```tsx filename="src/components/auth/login-form/login-form.tsx" {4,7} showLineNumbers +const loginSchema = z.object({ + email: z.string().email(), + password: z.string().min(3), + rememberMe: z.boolean().default(false), +}) + +type FormValues = z.infer +``` + +Огромный плюс валидационных схем в связке с react-hook-form в том, что мы разделяем ui (jsx) и +бизнес логику (валидация). Таким образом мы можем использовать одну и ту же валидационную схему для разных форм. + + + Хорошей практикой считается выносить схемы и типы в отдельные файлы, сделайте это сами. + diff --git a/pages/lesson-2/images/login-form-native-validation-show-errors.png b/pages/lesson-2/images/login-form-native-validation-show-errors.png new file mode 100644 index 0000000..820ed43 Binary files /dev/null and b/pages/lesson-2/images/login-form-native-validation-show-errors.png differ diff --git a/pages/lesson-2/images/login-form-native-validation.png b/pages/lesson-2/images/login-form-native-validation.png new file mode 100644 index 0000000..6394422 Binary files /dev/null and b/pages/lesson-2/images/login-form-native-validation.png differ diff --git a/pages/lesson-2/images/login-form-zod-validation-basic.png b/pages/lesson-2/images/login-form-zod-validation-basic.png new file mode 100644 index 0000000..fcbc13c Binary files /dev/null and b/pages/lesson-2/images/login-form-zod-validation-basic.png differ