Перейти к содержимому

21. Form: валидация

TanStack Form поддерживает валидацию на разных событиях:

<form.Field
name="email"
validators={{
onChange: ({ value }) => validateEmail(value), // При каждом вводе
onBlur: ({ value }) => strictValidate(value), // При потере фокуса
onMount: ({ value }) => initialValidate(value), // При монтировании
onSubmit: ({ value }) => finalValidate(value), // При отправке
onChangeAsync: async ({ value }) => await checkEmail(value), // Асинхронно при вводе
onChangeAsyncDebounceMs: 500, // Дебаунс для async
}}
>
validators={{
onChange: ({ value }) => {
if (!value) return 'Обязательное поле'
if (value.length < 3) return 'Минимум 3 символа'
if (value.length > 50) return 'Максимум 50 символов'
return undefined // undefined = нет ошибки
},
}}

Идеальна для проверок на сервере (уникальность email, username):

validators={{
onChangeAsync: async ({ value }) => {
if (!value) return undefined // Не проверяем пустые значения
const exists = await checkEmailExists(value)
if (exists) return 'Этот email уже зарегистрирован'
return undefined
},
onChangeAsyncDebounceMs: 500, // Ждём 500ms после ввода
}}
import { z } from 'zod'
const userSchema = z.object({
name: z.string().min(2, 'Минимум 2 символа'),
email: z.string().email('Невалидный email'),
age: z.number().min(18, 'Минимальный возраст 18 лет'),
})
// В useForm:
const form = useForm({
defaultValues: { name: '', email: '', age: 0 },
validators: {
onChange: userSchema, // Zod схема работает напрямую!
},
})

Валидация, которая зависит от значений других полей:

const form = useForm({
defaultValues: { password: '', confirmPassword: '' },
validators: {
onChange: ({ value }) => {
if (value.password !== value.confirmPassword) {
return { fields: { confirmPassword: 'Пароли не совпадают' } }
}
},
},
})

TanStack Form поддерживает популярные библиотеки через адаптеры:

import { zodValidator } from '@tanstack/zod-form-adapter'
import { valibotValidator } from '@tanstack/valibot-form-adapter'
const form = useForm({
validatorAdapter: zodValidator(),
validators: {
onChange: z.object({
email: z.string().email(),
}),
},
})
// Ошибки по источнику (errorMap — read-only, только для чтения)
const errorMap = field.state.meta.errorMap
// {
// onChange: 'Ошибка при вводе',
// onBlur: 'Ошибка при blur',
// onSubmit: 'Ошибка при submit',
// }
// Плоский массив всех ошибок
field.state.meta.errors // ['Ошибка при вводе', ...]