Валидация

Валидация пользовательского ввода является критически важной частью интерфейсов. Она обеспечивает корректность данных, предотвращает ошибки на стороне сервера и повышает удобство взаимодействия. При использовании компонентов Radix UI валидация строится вокруг нативных возможностей HTML-форм, возможностей React и гибкой композиционной архитектуры самой библиотеки.

Компоненты Radix UI не содержат встроенной бизнес-логики проверки данных. Их задача — предоставить доступные, управляемые и расширяемые примитивы интерфейса, поверх которых реализуется логика валидации.

Основные принципы:

  • Отделение логики от представления
  • Использование контролируемых компонентов
  • Интеграция с нативной валидацией браузера
  • Совместимость с библиотеками управления формами

Благодаря этому Radix UI одинаково хорошо работает с различными стратегиями проверки данных.


Виды валидации

В современных интерфейсах используются несколько типов проверки данных.

Нативная HTML-валидация

Основана на стандартных атрибутах элементов формы:

  • required
  • min
  • max
  • pattern
  • type="email"
  • type="number"

Преимущества:

  • минимальный объём кода
  • поддержка браузером
  • базовая доступность

Недостатки:

  • ограниченные возможности кастомизации
  • слабая интеграция со сложной логикой

Radix UI полностью совместим с этим подходом, поскольку использует настоящие HTML-элементы внутри своих примитивов.


Клиентская логическая валидация

Реализуется на уровне JavaScript/React.

Используется для:

  • проверки зависимых полей
  • сложных регулярных выражений
  • асинхронных проверок
  • валидации бизнес-правил

Асинхронная валидация

Часто применяется для:

  • проверки уникальности логина
  • проверки существования пользователя
  • валидации промокодов
  • проверки данных через API

Формы и компоненты Radix UI

Radix UI предоставляет набор примитивов для построения форм через пакет:

@radix-ui/react-form

Этот пакет решает несколько задач:

  • организация структуры формы
  • отображение ошибок
  • поддержка доступности
  • интеграция с нативной валидацией

Основные элементы:

Компонент Назначение
Form.Root контейнер формы
Form.Field логическое поле
Form.Label подпись
Form.Control элемент ввода
Form.Message сообщение об ошибке
Form.ValidityState состояние валидности

Базовая структура формы

Пример формы с валидацией.

import * as Form from "@radix-ui/react-form";

function LoginForm() {
  return (
    <Form.Root>
      <Form.Field name="email">
        <Form.Label>Email</Form.Label>
        <Form.Control asChild>
          <input type="email" required />
        </Form.Control>

        <Form.Message match="valueMissing">
          Поле обязательно
        </Form.Message>

        <Form.Message match="typeMismatch">
          Неверный формат email
        </Form.Message>
      </Form.Field>
    </Form.Root>
  );
}

Особенности архитектуры:

  • Form.Field связывает поле с валидацией
  • Form.Message отображается только при совпадении состояния
  • сообщения автоматически связаны с элементом ввода

Использование ValidityState

Radix UI использует стандартный API браузера:

HTMLInputElement.validity

Этот объект содержит множество флагов:

Свойство Значение
valueMissing обязательное поле не заполнено
typeMismatch неверный тип
patternMismatch несоответствие регулярному выражению
tooShort слишком короткое значение
tooLong слишком длинное значение
rangeUnderflow значение меньше минимума
rangeOverflow значение больше максимума

Radix UI позволяет отображать сообщение для любого из этих состояний.

<Form.Message match="tooShort">
Минимум 8 символов
</Form.Message>

Кастомная валидация

Иногда стандартных проверок недостаточно. В таких случаях используется JavaScript-логика.

Пример проверки длины пароля.

function validatePassword(value) {
  if (value.length < 8) {
    return "Пароль слишком короткий";
  }
  return null;
}

Интеграция в компонент:

function PasswordField() {
  const [error, setError] = React.useState(null);

  function handleChange(e) {
    const value = e.target.value;
    setError(validatePassword(value));
  }

  return (
    <Form.Field name="password">
      <Form.Label>Пароль</Form.Label>

      <Form.Control asChild>
        <input type="password" onCha nge={handleChange} />
      </Form.Control>

      {error && <div className="error">{error}</div>}
    </Form.Field>
  );
}

Контролируемые компоненты

В сложных формах используется управление состоянием через React.

function FormExample() {
  const [value, setValue] = React.useState("");

  return (
    <Form.Field name="username">
      <Form.Label>Имя пользователя</Form.Label>

      <Form.Control asChild>
        <input
          value={value}
          onCha nge={(e) => setValue(e.target.value)}
        />
      </Form.Control>
    </Form.Field>
  );
}

Такой подход позволяет:

  • хранить состояние формы
  • выполнять проверки в реальном времени
  • управлять отображением ошибок

Валидация при отправке формы

Radix UI использует нативное событие формы.

<Form.Root
  onSub mit={(event) => {
    event.preventDefault();
    console.log("submit");
  }}
>

Типичный алгоритм:

  1. пользователь нажимает кнопку
  2. браузер проверяет HTML-валидность
  3. Radix UI показывает сообщения
  4. при отсутствии ошибок происходит отправка

Асинхронная проверка данных

Пример проверки доступности имени пользователя.

async function checkUsername(username) {
  const response = await fetch(`/api/check?username=${username}`);
  const data = await response.json();

  return data.available;
}

Интеграция:

function UsernameField() {
  const [error, setError] = React.useState(null);

  async function handleBlur(e) {
    const available = await checkUsername(e.target.value);

    if (!available) {
      setError("Имя уже занято");
    } else {
      setError(null);
    }
  }

  return (
    <Form.Field name="username">
      <Form.Label>Username</Form.Label>

      <Form.Control asChild>
        <input onB lur={handleBlur} />
      </Form.Control>

      {error && <div>{error}</div>}
    </Form.Field>
  );
}

Асинхронная валидация обычно выполняется:

  • при потере фокуса
  • при отправке формы
  • после debounce

Валидация с использованием регулярных выражений

HTML поддерживает атрибут pattern.

<input pattern="[A-Za-z0-9]{6,}" required />

Radix UI позволяет реагировать на эту ошибку.

<Form.Message match="patternMismatch">
Минимум 6 символов без специальных знаков
</Form.Message>

Валидация сложных форм

При работе с большими формами обычно используются специализированные библиотеки:

  • React Hook Form
  • Formik
  • Zod
  • Yup

Radix UI отлично интегрируется с ними благодаря свойству asChild.


Интеграция с React Hook Form

Установка:

npm install react-hook-form

Пример:

import { useForm } from "react-hook-form";
import * as Form from "@radix-ui/react-form";

function Example() {
  const { register, handleSubmit, formState } = useForm();

  return (
    <Form.Root onSub mit={handleSubmit(console.log)}>
      <Form.Field name="email">
        <Form.Label>Email</Form.Label>

        <Form.Control asChild>
          <input
            {...register("email", {
              required: "Обязательное поле"
            })}
          />
        </Form.Control>

        {formState.errors.email && (
          <div>{formState.errors.email.message}</div>
        )}
      </Form.Field>
    </Form.Root>
  );
}

Групповая валидация

Иногда требуется проверка нескольких полей.

Пример — подтверждение пароля.

function validate(password, confirm) {
  if (password !== confirm) {
    return "Пароли не совпадают";
  }
  return null;
}

Использование:

const error = validate(password, confirmPassword);

Групповая логика обычно размещается:

  • при отправке формы
  • внутри form-schema
  • в библиотеке валидации

Отображение ошибок

Ошибки должны быть:

  • понятными
  • краткими
  • связанными с конкретным полем

Radix UI автоматически связывает:

  • label
  • input
  • error message

Это важно для screen readers.


Доступность (Accessibility)

Radix UI автоматически применяет:

  • aria-invalid
  • aria-describedby
  • правильную связь label и input

Пример:

<input aria-invalid="true" aria-describedby="error-id">

Это позволяет:

  • экранным дикторам озвучивать ошибки
  • пользователям клавиатуры получать корректную навигацию

Стилизация ошибок

Radix UI добавляет атрибуты состояния:

data-invalid
data-valid

Пример CSS:

input[data-invalid] {
  border-color: red;
}

.error {
  color: red;
  font-size: 14px;
}

Валидация в реальном времени

Часто используется проверка на вводе.

function handleChange(e) {
  const value = e.target.value;

  if (value.length < 3) {
    setError("Минимум 3 символа");
  } else {
    setError(null);
  }
}

Преимущества:

  • мгновенная обратная связь
  • меньше ошибок при отправке

Недостатки:

  • лишние вычисления
  • может раздражать пользователя

Поэтому часто используется debounce.


Debounce-валидация

Позволяет откладывать проверку.

const debouncedValidate = debounce(validate, 500);

Это снижает:

  • нагрузку на API
  • количество лишних проверок

Архитектура сложной системы валидации

Крупные приложения обычно используют архитектуру:

UI (Radix)
     ↓
Form state
     ↓
Validation schema
     ↓
API validation

Типичный стек:

Radix UI
React Hook Form
Zod

Пример схемы Zod:

const schema = z.object({
  email: z.string().email(),
  password: z.string().min(8)
});

Лучшие практики

1. Разделение логики

UI не должен содержать бизнес-валидацию.


2. Использование схем

Schema-validation упрощает поддержку.


3. Минимизация асинхронных запросов

Использование debounce.


4. Чёткие сообщения

Ошибка должна объяснять:

  • что произошло
  • как исправить

5. Повторная проверка на сервере

Клиентская валидация — лишь удобство интерфейса.


Типичные ошибки

Дублирование логики

Одна и та же проверка:

  • в компоненте
  • в форме
  • на сервере

Это усложняет поддержку.


Валидация только на клиенте

Без серверной проверки безопасность невозможна.


Плохие сообщения об ошибках

Сообщения вида:

Invalid input

не дают понимания проблемы.


Проверка при каждом символе

Может приводить к:

  • множеству API-запросов
  • плохой производительности

Итоговая модель валидации в Radix UI

Radix UI предоставляет:

  • доступные примитивы формы
  • интеграцию с HTML-валидацией
  • гибкое отображение ошибок
  • совместимость с внешними библиотеками

Валидация при этом строится как отдельный слой приложения, позволяющий использовать любые подходы проверки данных без ограничений со стороны интерфейсных компонентов.