Связка с формами

Radix UI предоставляет высоко настраиваемые, доступные компоненты пользовательского интерфейса, которые можно использовать внутри форм React. Ключевой аспект работы с формами — управление состоянием, валидацией и взаимодействием компонентов с библиотеками управления формами, такими как react-hook-form или formik.

Radix UI сам по себе не включает внутренние механизмы управления формами, поэтому важно понимать, как синхронизировать его контролируемые и неконтролируемые компоненты с форм-менеджером.


Использование Radix UI компонентов внутри форм

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

Контролируемый компонент получает значение через value и уведомляет о изменениях через onChange. Пример на базе Switch:

import * as React from "react";
import * as Switch from "@radix-ui/react-switch";

function ControlledSwitch({ value, onChange }) {
  return (
    <Switch.Root
      checked={value}
      onCheckedCha nge={onChange}
      className="switch-root"
    >
      <Switch.Thumb className="switch-thumb" />
    </Switch.Root>
  );
}

Для интеграции с react-hook-form используется метод Controller, который связывает Radix UI с системой управления формой:

import { useForm, Controller } from "react-hook-form";

function FormExample() {
  const { handleSubmit, control } = useForm({
    defaultValues: { newsletter: false }
  });

  const onSub mit = (data) => console.log(data);

  return (
    <form onSub mit={handleSubmit(onSubmit)}>
      <Controller
        name="newsletter"
        control={control}
        render={({ field }) => (
          <ControlledSwitch value={field.value} onCha nge={field.onChange} />
        )}
      />
      <button type="submit">Отправить</button>
    </form>
  );
}

Ключевой момент: все свойства Radix UI должны быть синхронизированы с field.value и field.onChange для корректного отражения состояния формы.


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

Неконтролируемые компоненты используют внутреннее состояние и уведомляют об изменениях через колбэки. Для таких компонентов важно использовать их API событий:

<Switch.Root
  defaultChecked
  onCheckedCha nge={(checked) => console.log("Checked:", checked)}
>
  <Switch.Thumb />
</Switch.Root>

При интеграции с формой можно отслеживать эти события и обновлять состояние формы вручную.


Интеграция с react-hook-form и Formik

Radix UI хорошо сочетается с react-hook-form благодаря удобному контролю состояния через Controller. Важные моменты:

  • Controller — обязательный для контролируемых компонентов, которые не используют стандартные HTML input.
  • defaultValue — должно соответствовать типу компонента (boolean для Switch, строка для Input и т. д.).
  • Валидация — в react-hook-form указывается через rules в Controller, что позволяет централизованно управлять ошибками.

Для Formik интеграция схожа: компонент получает value и onChange из formik.getFieldProps или напрямую через setFieldValue.

import { useFormik } from "formik";
import * as Checkbox from "@radix-ui/react-checkbox";

function FormikForm() {
  const formik = useFormik({
    initialValues: { agree: false },
    onSubmit: (values) => console.log(values)
  });

  return (
    <form onSub mit={formik.handleSubmit}>
      <Checkbox.Root
        checked={formik.values.agree}
        onCheckedCha nge={(value) => formik.setFieldValue("agree", value)}
      >
        <Checkbox.Indicator />
      </Checkbox.Root>
      <button type="submit">Отправить</button>
    </form>
  );
}

Валидация и отображение ошибок

Radix UI предоставляет низкоуровневые визуальные компоненты, но не управляет ошибками. Их отображение нужно реализовать вручную:

{errors.agree && (
  <span className="error-message">{errors.agree.message}</span>
)}

Важно отделять логику формы от визуального состояния компонентов Radix UI: компоненты отвечают за доступность и визуальный контроль, форма — за данные и ошибки.


Примеры комплексных форм

Комбинация различных компонентов Radix UI в одной форме:

import * as Checkbox from "@radix-ui/react-checkbox";
import * as Switch from "@radix-ui/react-switch";
import * as RadioGroup from "@radix-ui/react-radio-group";
import { useForm, Controller } from "react-hook-form";

function ComplexForm() {
  const { control, handleSubmit } = useForm({
    defaultValues: { agree: false, newsletter: true, gender: "male" }
  });

  const onSub mit = (data) => console.log(data);

  return (
    <form onSub mit={handleSubmit(onSubmit)}>
      <Controller
        name="agree"
        control={control}
        render={({ field }) => (
          <Checkbox.Root checked={field.value} onCheckedCha nge={field.onChange}>
            <Checkbox.Indicator />
          </Checkbox.Root>
        )}
      />

      <Controller
        name="newsletter"
        control={control}
        render={({ field }) => (
          <Switch.Root checked={field.value} onCheckedCha nge={field.onChange}>
            <Switch.Thumb />
          </Switch.Root>
        )}
      />

      <Controller
        name="gender"
        control={control}
        render={({ field }) => (
          <RadioGroup.Root value={field.value} onValueCha nge={field.onChange}>
            <RadioGroup.Item value="male" />
            <RadioGroup.Item value="female" />
          </RadioGroup.Root>
        )}
      />

      <button type="submit">Отправить</button>
    </form>
  );
}

Здесь показано, как использовать несколько типов компонентов Radix UI одновременно, сохраняя полное управление состоянием формы.


Основные рекомендации

  • Использовать Controller для всех неклассических HTML элементов.
  • Разделять визуальное состояние и данные формы.
  • Учитывать доступность (aria атрибуты, focus management), которую Radix UI обеспечивает по умолчанию.
  • Поддерживать согласованность типов данных между компонентами и формой (boolean, string, number).
  • Реализовать централизованное отображение ошибок и сообщений валидации.

Radix UI позволяет создавать формы с современным интерфейсом, доступные и полностью управляемые, если правильно связать его с системой управления формой.