REACT_LIB Telegram 713
Формы без боли: react-hook-form + zod = схема в центре, минимум ререндеров

Сейчас покажу, как я собираю формы так, чтобы валидация была в одном месте, а компоненты не прыгали от каждого ввода.

1) Схема — источник правды


import { z } from 'zod';

export const userSchema = z.object({
email: z.string().email(),
age: z.number().int().min(18),
newsletter: z.boolean().default(false),
});
export type UserForm = z.infer<typeof userSchema>;


2) Инициализация формы с резолвером


import { useForm, FormProvider } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';

const UserForm = ({ initial }: { initial?: Partial<UserForm> }) => {
const methods = useForm<UserForm>({
resolver: zodResolver(userSchema),
defaultValues: { newsletter: false, ...initial },
mode: 'onChange', // мгновенная подсветка ошибок
});

const { handleSubmit, formState: { isSubmitting, errors } } = methods;

const onSubmit = async (data: UserForm) => {
// маппим серверные ошибки обратно в форму при необходимости
// setError('email', { type: 'server', message: 'уже занято' })
await api.saveUser(data);
};

return (
<FormProvider {...methods}>
<form onSubmit={handleSubmit(onSubmit)}>
<Input name="email" label="Email" />
<NumberInput name="age" label="Возраст" />
<Checkbox name="newsletter" label="Подписаться" />
{errors.root && <p className="error">{errors.root.message}</p>}
<button disabled={isSubmitting}>Сохранить</button>
</form>
</FormProvider>
);
};


3) Поля - через контекст, с мемоизацией


import { useFormContext, Controller } from 'react-hook-form';

// Простой контролируемый инпут с минимальными ререндерами
export const Input = React.memo(({ name, label }: { name: string; label: string }) => {
const { register, formState: { errors } } = useFormContext();
return (
<label>
{label}
<input {...register(name)} />
{errors[name] && <span className="error">{String(errors[name]?.message)}</span>}
</label>
);
});

// Пример для нестандартного компонента через Controller
export const NumberInput = React.memo(({ name, label }: { name: string; label: string }) => {
const { control, formState: { errors } } = useFormContext();
return (
<label>
{label}
<Controller
control={control}
name={name}
render={({ field }) => <input type="number" {...field} />}
/>
{errors[name] && <span className="error">{String(errors[name]?.message)}</span>}
</label>
);
});


4) Динамические списки - useFieldArray


const { control } = useFormContext();
const { fields, append, remove } = useFieldArray({ control, name: 'phones' });


Храните массивы в форме, рендерите по fields, добавляйте/удаляйте кнопками - ререндерится только нужный участок.

5) Практические советы

- Включайте DevTools формы только в dev.
- Ошибки сервера маппьте через setError, а не кидайте alert.
- При редактировании сущности меняйте key формы (<form key={user.id}>) — проще, чем делать reset в куче мест.
- Дорогие поля оборачивайте в React.memo, а вычисления — в useMemo.

✍️ @React_lib
5👍2



tgoop.com/React_lib/713
Create:
Last Update:

Формы без боли: react-hook-form + zod = схема в центре, минимум ререндеров

Сейчас покажу, как я собираю формы так, чтобы валидация была в одном месте, а компоненты не прыгали от каждого ввода.

1) Схема — источник правды


import { z } from 'zod';

export const userSchema = z.object({
email: z.string().email(),
age: z.number().int().min(18),
newsletter: z.boolean().default(false),
});
export type UserForm = z.infer<typeof userSchema>;


2) Инициализация формы с резолвером


import { useForm, FormProvider } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';

const UserForm = ({ initial }: { initial?: Partial<UserForm> }) => {
const methods = useForm<UserForm>({
resolver: zodResolver(userSchema),
defaultValues: { newsletter: false, ...initial },
mode: 'onChange', // мгновенная подсветка ошибок
});

const { handleSubmit, formState: { isSubmitting, errors } } = methods;

const onSubmit = async (data: UserForm) => {
// маппим серверные ошибки обратно в форму при необходимости
// setError('email', { type: 'server', message: 'уже занято' })
await api.saveUser(data);
};

return (
<FormProvider {...methods}>
<form onSubmit={handleSubmit(onSubmit)}>
<Input name="email" label="Email" />
<NumberInput name="age" label="Возраст" />
<Checkbox name="newsletter" label="Подписаться" />
{errors.root && <p className="error">{errors.root.message}</p>}
<button disabled={isSubmitting}>Сохранить</button>
</form>
</FormProvider>
);
};


3) Поля - через контекст, с мемоизацией


import { useFormContext, Controller } from 'react-hook-form';

// Простой контролируемый инпут с минимальными ререндерами
export const Input = React.memo(({ name, label }: { name: string; label: string }) => {
const { register, formState: { errors } } = useFormContext();
return (
<label>
{label}
<input {...register(name)} />
{errors[name] && <span className="error">{String(errors[name]?.message)}</span>}
</label>
);
});

// Пример для нестандартного компонента через Controller
export const NumberInput = React.memo(({ name, label }: { name: string; label: string }) => {
const { control, formState: { errors } } = useFormContext();
return (
<label>
{label}
<Controller
control={control}
name={name}
render={({ field }) => <input type="number" {...field} />}
/>
{errors[name] && <span className="error">{String(errors[name]?.message)}</span>}
</label>
);
});


4) Динамические списки - useFieldArray


const { control } = useFormContext();
const { fields, append, remove } = useFieldArray({ control, name: 'phones' });


Храните массивы в форме, рендерите по fields, добавляйте/удаляйте кнопками - ререндерится только нужный участок.

5) Практические советы

- Включайте DevTools формы только в dev.
- Ошибки сервера маппьте через setError, а не кидайте alert.
- При редактировании сущности меняйте key формы (<form key={user.id}>) — проще, чем делать reset в куче мест.
- Дорогие поля оборачивайте в React.memo, а вычисления — в useMemo.

✍️ @React_lib

BY React




Share with your friend now:
tgoop.com/React_lib/713

View MORE
Open in Telegram


Telegram News

Date: |

3How to create a Telegram channel? SUCK Channel Telegram The channel also called on people to turn out for illegal assemblies and listed the things that participants should bring along with them, showing prior planning was in the works for riots. The messages also incited people to hurl toxic gas bombs at police and MTR stations, he added. best-secure-messaging-apps-shutterstock-1892950018.jpg To upload a logo, click the Menu icon and select “Manage Channel.” In a new window, hit the Camera icon.
from us


Telegram React
FROM American