Files
flashcards-docs/pages/lesson-2/chapter-3.ru.mdx

171 lines
5.2 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Callout } from 'nextra/components'
# Рефакторинг
Проблема:
- Компонент LoginForm стал слишком сложным, в нем много логики завязанной на формах, и всему виной useController.
Решение:
- Создать автономные компоненты `ControlledCheckbox` и `ControlledTextField` и использовать их в `LoginForm`
## ControlledCheckbox
### Минимально рабочая реализация
Создадим файл `controlled-checkbox.tsx` и вынесем туда логику `useController` и `Checkbox`:
```tsx filename="src/components/ui/controlled/controlled-checkbox/controlled-checkbox.tsx"
import { useController, UseControllerProps } from 'react-hook-form'
import { Checkbox, CheckboxProps } from '@/components'
export type ControlledCheckboxProps = UseControllerProps<any> &
Omit<CheckboxProps, 'onChange' | 'value' | 'id'>
export const ControlledCheckbox = ({
name,
rules,
shouldUnregister,
control,
defaultValue,
...checkboxProps
}: ControlledCheckboxProps) => {
const {
field: { onChange, value },
} = useController({
name,
rules,
shouldUnregister,
control,
defaultValue,
})
return (
<Checkbox
{...{
onChange,
checked: value,
id: name,
...checkboxProps,
}}
/>
)
}
```
<Callout>В UseControllerProps пока что передадим any</Callout>
Уберем из `LoginForm` логику `useController` и заменим на `ControlledCheckbox`:
```tsx filename="src/components/auth/login-form/login-form.tsx" showLineNumbers {12}
import { ControlledCheckbox } from '@/components'
// ...
return (
<form onSubmit={handleSubmit(onSubmit)}>
<TextField {...register('email')} label={'email'} errorMessage={errors.email?.message} />
<TextField
{...register('password')}
label={'password'}
errorMessage={errors.password?.message}
/>
<ControlledCheckbox label={'remember me'} control={control} name={'rememberMe'} />
<Button type="submit">Submit</Button>
</form>
)
```
Проп `name` отвечает за название поля в форме, а `control` за то, какой контроллер будет использоваться.
<Callout>Убедитесь что все работает как надо</Callout>
### Типизация
Проблема:
Сейчас в качестве control и name можно передать что угодно, а это может привести к ошибкам которые тяжело дебажить.
Теперь давайте типизируем `ControlledCheckboxProps`, для этого воспользуемся дженериками:
```tsx filename="src/components/ui/controlled/controlled-checkbox/controlled-checkbox.tsx" showLineNumbers {1,5,6,8,15}
import { FieldValues, useController, UseControllerProps } from 'react-hook-form'
import { Checkbox, CheckboxProps } from '@/components'
export type ControlledCheckboxProps<TFieldValues extends FieldValues> =
UseControllerProps<TFieldValues> & Omit<CheckboxProps, 'onChange' | 'value' | 'id'>
export const ControlledCheckbox = <TFieldValues extends FieldValues>({
name,
rules,
shouldUnregister,
control,
defaultValue,
...checkboxProps
}: ControlledCheckboxProps<TFieldValues>) => {
// ...
}
```
Попробуем допустить ошибку в `name`:
![controlled-checkbox-typescript-working](./images/controlled-checkbox-typescript-working.png)
Все работает как и ожидалось :)
## React-hook-form devtools
Проблема:
- Дебажить через консоль неудобно, хочется видеть что происходит в форме в реальном времени.
Решение:
- Использовать [react-hook-form devtools](https://www.react-hook-form.com/dev-tools/)
### Установка
Установим devtools:
```bash filename="Terminal"
pnpm i -D @hookform/devtools
```
### Использование
И добавим их в `LoginForm`:
```tsx filename="src/components/auth/login-form/login-form.tsx" showLineNumbers {1,7}
import { DevTool } from '@hookform/devtools'
// ...
return (
<form onSubmit={handleSubmit(onSubmit)}>
<DevTool control={control} />
<TextField {...register('email')} label={'email'} errorMessage={errors.email?.message} />
<TextField
{...register('password')}
label={'password'}
errorMessage={errors.password?.message}
/>
<ControlledCheckbox label={'remember me'} control={control} name={'rememberMe'} />
<Button type="submit">Submit</Button>
</form>
)
```
### Скриншоты
Теперь у нас появилась кнопка, при нажатии на которую открывается панель с информацией о форме:
![react-hook-form-devtools-button](./images/rhf-devtool-button.png)
![react-hook-form-devtools-panel](./images/rhf-devtool-panel.png)
<Callout type={'info'}>
Эта панель будет доступна только в разработке, в опубликованном проекте кнопки не будет.
</Callout>