lesson 2, chapter 3: refactoring

This commit is contained in:
2023-07-29 15:20:21 +02:00
parent 2247328e2c
commit a417ae365d
6 changed files with 173 additions and 1 deletions

View File

@@ -1,4 +1,5 @@
{
"chapter-1": "Глава 1. React-hook-form",
"chapter-2": "Глава 2. Валидация форм"
"chapter-2": "Глава 2. Валидация форм",
"chapter-3": "Глава 3. Рефакторинг"
}

View File

@@ -0,0 +1 @@
# Under construction

View File

@@ -0,0 +1,170 @@
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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB