Управление состоянием checked

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

В библиотеке Radix UI управление состоянием checked реализовано через набор примитивов, которые обеспечивают:

  • доступность (ARIA-совместимость);
  • предсказуемое управление состоянием;
  • поддержку контролируемых и неконтролируемых компонентов;
  • корректную синхронизацию с DOM.

Наиболее активно состояние checked применяется в следующих компонентах Radix UI:

  • Checkbox
  • Switch
  • Radio Group
  • Toggle
  • Toggle Group

Каждый из этих компонентов реализует схожую модель состояния, но с разными правилами поведения.


Основы состояния checked

В Radix UI состояние выбора может быть представлено тремя значениями:

true
false
"indeterminate"

Значения интерпретируются следующим образом:

Значение Смысл
true элемент выбран
false элемент не выбран
"indeterminate" промежуточное состояние

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

Radix UI автоматически синхронизирует это состояние с ARIA-атрибутами:

aria-checked="true"
aria-checked="false"
aria-checked="mixed"

Это обеспечивает корректную работу со скринридерами.


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

Все компоненты Radix UI, поддерживающие checked, могут работать в двух режимах:

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

Состояние хранится внутри компонента.

import * as Checkbox from "@radix-ui/react-checkbox";

<Checkbox.Root defaultChecked />

defaultChecked устанавливает начальное состояние, после чего компонент самостоятельно управляет изменениями.

Особенности:

  • внутренний useState
  • автоматическая обработка кликов
  • минимальная конфигурация

Контролируемый режим

Состояние хранится во внешнем состоянии приложения.

const [checked, setChecked] = useState(false);

<Checkbox.Root
  checked={checked}
  onCheckedCha nge={setChecked}
/>

Особенности контролируемого режима:

  • источник истины находится в React-состоянии
  • компонент становится полностью предсказуемым
  • легко синхронизируется с сервером или глобальным состоянием

Компонент Checkbox

Checkbox — основной элемент управления состоянием checked.

Минимальная структура компонента:

import * as Checkbox from "@radix-ui/react-checkbox";

<Checkbox.Root>
  <Checkbox.Indicator />
</Checkbox.Root>

Root

Checkbox.Root отвечает за:

  • хранение состояния
  • обработку кликов
  • управление доступностью

Поддерживаемые свойства:

Свойство Назначение
checked контролируемое состояние
defaultChecked начальное состояние
onCheckedChange обработчик изменения
disabled отключение элемента
required обязательный ввод

Indicator

Checkbox.Indicator отображает визуальное состояние чекбокса.

<Checkbox.Root>
  <Checkbox.Indicator>
    ✓
  </Checkbox.Indicator>
</Checkbox.Root>

Indicator появляется только тогда, когда состояние checked = true.


Обработка изменения состояния

Radix UI предоставляет единый обработчик:

onCheckedChange

Пример:

const handleChange = (value) => {
  console.log(value);
};

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

<Checkbox.Root onCheckedCha nge={handleChange} />

Аргумент может принимать значения:

true
false
"indeterminate"

Это отличает Radix UI от стандартного HTML-чекбокса, где состояние бинарное.


Промежуточное состояние (Indeterminate)

Промежуточное состояние применяется в сложных интерфейсах выбора.

Пример:

const [checked, setChecked] = useState("indeterminate");

<Checkbox.Root
  checked={checked}
  onCheckedCha nge={setChecked}
/>

Визуально это состояние обычно отображается горизонтальной линией вместо галочки.


Пример дерева выбора

Рассмотрим структуру:

Все файлы
 ├ Документ 1
 ├ Документ 2
 └ Документ 3

Логика состояния:

Состояние дочерних Родитель
все выбраны checked
ни один не выбран unchecked
частично выбраны indeterminate

Radix UI позволяет явно установить:

checked="indeterminate"

Data-атрибуты состояния

Radix UI автоматически добавляет data-атрибуты, которые удобно использовать для стилизации.

data-state="checked"
data-state="unchecked"
data-state="indeterminate"

Пример CSS:

[data-state="checked"] {
  background: black;
}

[data-state="indeterminate"] {
  background: gray;
}

Это позволяет строить стили без JavaScript.


Интеграция с формами

Radix UI поддерживает отправку значений формы.

Для этого используется свойство:

name

Пример:

<Checkbox.Root name="terms" value="accepted">
  <Checkbox.Indicator />
</Checkbox.Root>

При отправке формы браузер отправит:

terms=accepted

если чекбокс выбран.


Компонент Switch

Switch — это альтернативная визуализация состояния checked, представляющая собой переключатель.

Структура:

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

<Switch.Root>
  <Switch.Thumb />
</Switch.Root>

Состояние переключателя

Switch использует те же свойства:

checked
defaultChecked
onCheckedChange

Пример:

const [enabled, setEnabled] = useState(false);

<Switch.Root
  checked={enabled}
  onCheckedCha nge={setEnabled}
>
  <Switch.Thumb />
</Switch.Root>

Radio Group и checked

В RadioGroup состояние checked реализуется иначе.

Выбран может быть только один элемент.

Структура:

import * as RadioGroup from "@radix-ui/react-radio-group";

<RadioGroup.Root>
  <RadioGroup.Item value="a" />
  <RadioGroup.Item value="b" />
</RadioGroup.Root>

Управление выбором

const [value, setValue] = useState("a");

<RadioGroup.Root
  value={value}
  onValueCha nge={setValue}
>

Каждый элемент автоматически получает состояние:

data-state="checked"

или

data-state="unchecked"

Toggle и состояние pressed

Компонент Toggle концептуально похож на Checkbox, но использует состояние:

pressed

Тем не менее логика полностью соответствует checked.

Пример:

import * as Toggle from "@radix-ui/react-toggle";

<Toggle.Root>
  Bold
</Toggle.Root>

Контролируемый режим:

const [pressed, setPressed] = useState(false);

<Toggle.Root
  pressed={pressed}
  onPressedCha nge={setPressed}
/>

Toggle Group

ToggleGroup позволяет создавать группы переключателей.

Режимы:

  • single
  • multiple

Single режим

<ToggleGroup.Root type="single">
  <ToggleGroup.Item value="left" />
  <ToggleGroup.Item value="center" />
  <ToggleGroup.Item value="right" />
</ToggleGroup.Root>

Здесь активен только один элемент.


Multiple режим

<ToggleGroup.Root type="multiple">
  <ToggleGroup.Item value="bold" />
  <ToggleGroup.Item value="italic" />
</ToggleGroup.Root>

Здесь несколько элементов могут быть выбраны одновременно.


Клавиатурная доступность

Radix UI реализует полную поддержку клавиатуры.

Основные клавиши:

Клавиша Действие
Space переключить checked
Enter активировать элемент
Tab переход между элементами
Arrow Keys навигация в RadioGroup

ARIA-атрибуты добавляются автоматически:

role="checkbox"
role="switch"
role="radio"

Внутренняя модель состояния

Под капотом Radix UI использует утилиту:

useControllableState

Она обеспечивает единый API для:

  • checked
  • defaultChecked
  • onCheckedChange

Алгоритм работы:

  1. Проверяется наличие checked.
  2. Если он есть — компонент контролируемый.
  3. Если нет — используется defaultChecked.
  4. Изменения передаются через callback.

Псевдокод:

const [value, setValue] = useControllableState({
  prop: checked,
  defaultProp: defaultChecked,
  onChange: onCheckedChange
});

Стилизация состояний

Radix UI не содержит встроенных стилей.

Для оформления используется CSS.

Пример:

.CheckboxRoot {
  width: 20px;
  height: 20px;
  border: 1px solid black;
}

.CheckboxRoot[data-state="checked"] {
  background: black;
}

.CheckboxRoot[data-state="indeterminate"] {
  background: gray;
}

Анимация состояния

Поскольку data-state меняется автоматически, можно легко добавить анимацию.

.CheckboxIndicator {
  transition: transform 150ms ease;
}

[data-state="checked"] .CheckboxIndicator {
  transform: scale(1);
}

[data-state="unchecked"] .CheckboxIndicator {
  transform: scale(0);
}

Управление состоянием через внешние хранилища

Контролируемая модель позволяет интегрировать checked с любыми стейт-менеджерами.

Пример с Zustand:

const useStore = create((set) => ({
  checked: false,
  setChecked: (value) => set({ checked: value })
}));

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

const { checked, setChecked } = useStore();

<Checkbox.Root
  checked={checked}
  onCheckedCha nge={setChecked}
/>

Синхронизация нескольких чекбоксов

Пример управления несколькими элементами:

const [items, setItems] = useState({
  a: false,
  b: false,
  c: false
});

Изменение:

const toggle = (key) => {
  setItems({
    ...items,
    [key]: !items[key]
  });
};

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

<Checkbox.Root
  checked={items.a}
  onCheckedCha nge={() => toggle("a")}
/>

Типизация в TypeScript

Тип значения checked в Radix UI:

type CheckedState = boolean | "indeterminate";

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

const [checked, setChecked] = useState<CheckedState>(false);

Обработчик:

const handleChange = (value: CheckedState) => {
  setChecked(value);
};

Производительность

Radix UI оптимизирован для минимальных перерисовок:

  • события локализованы внутри компонента
  • состояние синхронизируется через useControllableState
  • DOM обновляется только при изменении checked

Это позволяет безопасно использовать сотни элементов в больших списках.


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

Использование checked без onCheckedChange

<Checkbox.Root checked={true} />

Компонент станет read-only.


Смешивание контролируемого и неконтролируемого режима

Нельзя одновременно использовать:

checked
defaultChecked

Неправильная обработка indeterminate

setChecked(!checked)

Это работает только для boolean.

Правильный вариант:

setChecked(checked === true ? false : true)

Архитектурная роль checked в Radix UI

Состояние checked является фундаментальной частью системы примитивов Radix UI.

Оно обеспечивает:

  • единообразный API для элементов выбора;
  • доступность без дополнительной настройки;
  • гибкость контролируемых компонентов;
  • простую интеграцию с React-экосистемой;
  • предсказуемую работу интерфейсов.

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