Множественный выбор — один из базовых паттернов пользовательских интерфейсов. Он используется в случаях, когда необходимо предоставить пользователю возможность выбрать несколько элементов из набора, в отличие от радиокнопок, допускающих только одно значение.
Типичные сценарии применения:
В библиотеке Radix UI множественный выбор реализуется через несколько компонентов:
Каждый из них решает разные задачи и обладает собственной архитектурой взаимодействия.
Компонент ToggleGroup позволяет создавать группы кнопок, которые могут находиться в активном или неактивном состоянии.
Radix UI поддерживает два режима работы:
type="single" — только одно значениеtype="multiple" — множественный выборДля реализации множественного выбора используется режим
multiple.
import * as ToggleGroup fr om "@radix-ui/react-toggle-group";
function Example() {
return (
<ToggleGroup.Root type="multiple">
<ToggleGroup.Item value="bold">B</ToggleGroup.Item>
<ToggleGroup.Item value="italic">I</ToggleGroup.Item>
<ToggleGroup.Item value="underline">U</ToggleGroup.Item>
</ToggleGroup.Root>
);
}
Компоненты структуры:
| Компонент | Назначение |
|---|---|
ToggleGroup.Root |
контейнер группы |
ToggleGroup.Item |
элемент выбора |
При использовании type="multiple":
Item имеет значение valueПример состояния:
["bold", "italic"]
Это означает, что активированы два элемента.
Radix UI поддерживает два режима управления состоянием.
<ToggleGroup.Root type="multiple" defaultValue={["bold"]}>
<ToggleGroup.Item value="bold">B</ToggleGroup.Item>
<ToggleGroup.Item value="italic">I</ToggleGroup.Item>
</ToggleGroup.Root>
Особенности:
defaultValueconst [formats, setFormats] = useState(["bold"]);
<ToggleGroup.Root
type="multiple"
value={formats}
onValueCha nge={setFormats}
>
<ToggleGroup.Item value="bold">B</ToggleGroup.Item>
<ToggleGroup.Item value="italic">I</ToggleGroup.Item>
</ToggleGroup.Root>
Особенности:
Функция onValueChange получает массив активных
значений.
function handleChange(values) {
console.log(values);
}
Использование:
<ToggleGroup.Root
type="multiple"
onValueCha nge={handleChange}
>
При каждом изменении выводится текущий список выбранных элементов.
Radix UI изначально реализует корректную доступность.
ToggleGroup автоматически добавляет:
role="group"aria-pressedКаждый элемент:
aria-pressed="true"
или
aria-pressed="false"
Это позволяет экранным дикторам корректно озвучивать состояние.
Поддержка клавиатуры встроена.
Доступные действия:
| Клавиша | Действие |
|---|---|
| Tab | переход к группе |
| ArrowLeft | предыдущий элемент |
| ArrowRight | следующий элемент |
| Space | переключение |
| Enter | переключение |
Это соответствует стандартам WAI-ARIA Toggle Button Group.
Radix UI добавляет атрибут состояния:
data-state="on"
или
data-state="off"
Это позволяет легко стилизовать активные кнопки.
Пример CSS:
[data-state="on"] {
background: black;
color: white;
}
Radix UI не ограничивает количество выбранных элементов, однако это можно реализовать логикой приложения.
Пример:
function handleChange(values) {
if (values.length <= 3) {
setFormats(values);
}
}
Теперь можно выбрать максимум три элемента.
Один из самых распространённых сценариев — фильтрация.
Пример:
const [filters, setFilters] = useState([]);
<ToggleGroup.Root
type="multiple"
value={filters}
onValueCha nge={setFilters}
>
<ToggleGroup.Item value="react">React</ToggleGroup.Item>
<ToggleGroup.Item value="vue">Vue</ToggleGroup.Item>
<ToggleGroup.Item value="angular">Angular</ToggleGroup.Item>
</ToggleGroup.Root>
Выбранные фильтры:
["react", "angular"]
Затем используется фильтрация данных:
items.filter(item => filters.includes(item.framework))
Checkbox — наиболее классический способ множественного выбора.
Radix UI предоставляет компонент:
@radix-ui/react-checkbox
import * as Checkbox from "@radix-ui/react-checkbox";
function Example() {
return (
<label>
<Checkbox.Root value="email">
<Checkbox.Indicator />
</Checkbox.Root>
Email notifications
</label>
);
}
const [checked, setChecked] = useState(false);
<Checkbox.Root
checked={checked}
onCheckedCha nge={setChecked}
>
<Checkbox.Indicator />
</Checkbox.Root>
Для множественного выбора используется массив.
const [selected, setSelected] = useState([]);
Функция изменения:
function toggle(value) {
setSelected(prev =>
prev.includes(value)
? prev.filter(v => v !== value)
: [...prev, value]
);
}
Использование:
<Checkbox.Root
checked={selected.includes("email")}
onCheckedCha nge={() => toggle("email")}
>
<Checkbox.Indicator />
</Checkbox.Root>
В выпадающих меню часто требуется выбирать несколько элементов.
Radix UI предоставляет:
DropdownMenu.CheckboxItem
import * as DropdownMenu from "@radix-ui/react-dropdown-menu";
function Example() {
return (
<DropdownMenu.Root>
<DropdownMenu.Trigger>
Filters
</DropdownMenu.Trigger>
<DropdownMenu.Content>
<DropdownMenu.CheckboxItem checked>
React
</DropdownMenu.CheckboxItem>
<DropdownMenu.CheckboxItem>
Vue
</DropdownMenu.CheckboxItem>
</DropdownMenu.Content>
</DropdownMenu.Root>
);
}
const [filters, setFilters] = useState(["react"]);
checked={filters.includes("react")}
Изменение:
onCheckedCha nge={(checked) =>
updateFilter("react", checked)
}
Radix UI не предоставляет готовый multi-select, однако его легко построить на базе:
Архитектура:
Trigger
↓
Popover
↓
Список элементов
↓
Checkbox
<Popover.Root>
<Popover.Trigger>
Select frameworks
</Popover.Trigger>
<Popover.Content>
<Checkbox value="react" />
<Checkbox value="vue" />
<Checkbox value="angular" />
</Popover.Content>
</Popover.Root>
Выбранные элементы можно отображать в виде тегов.
Пример:
selected.map(item => (
<Tag key={item}>{item}</Tag>
))
При большом количестве элементов (1000+) важно учитывать производительность.
Основные методы оптимизации:
Использование библиотек:
Позволяет рендерить только видимые элементы.
const Item = React.memo(function Item({ value }) {
...
});
Это предотвращает лишние перерисовки.
const toggle = useCallback((value) => {
...
}, []);
Часто требуется кнопка:
Select all
Пример:
function selectAll() {
setSelected(items.map(i => i.id));
}
Clear selection
setSelected([]);
Checkbox поддерживает состояние:
indeterminate
Это полезно при иерархических структурах.
Пример:
Parent
├ Child
├ Child
└ Child
Если выбран только один дочерний элемент, родитель имеет состояние
indeterminate.
Radix UI можно использовать для построения древовидных структур.
Пример:
Folder
├ File
├ File
└ File
Логика:
Пример кнопок форматирования:
.toggle-group {
display: flex;
gap: 4px;
}
.toggle-item {
padding: 6px 10px;
border: 1px solid #ccc;
}
.toggle-item[data-state="on"] {
background: black;
color: white;
}
Анимацию можно добавить через CSS.
.toggle-item {
transition: background 0.2s;
}
Или через библиотеку:
Framer Motion
single вместо multipletype="single"
В этом случае массив значений не работает.
Каждый элемент должен иметь уникальный value.
Неправильно:
value="react"
value="react"
Ошибка возникает, если ключи списка нестабильны.
key={index}
Следует использовать уникальный идентификатор.
Radix придерживается принципов:
Это означает:
| Сценарий | Компонент |
|---|---|
| панель кнопок | ToggleGroup |
| форма | Checkbox |
| фильтр в меню | DropdownMenu.CheckboxItem |
| сложный селектор | Popover + Checkbox |
| поиск с выбором | Combobox |
<ToggleGroup.Root
type="multiple"
className="toolbar"
>
<ToggleGroup.Item value="bold">
Bold
</ToggleGroup.Item>
<ToggleGroup.Item value="italic">
Italic
</ToggleGroup.Item>
<ToggleGroup.Item value="underline">
Underline
</ToggleGroup.Item>
</ToggleGroup.Root>
Результат:
Radix UI легко интегрируется с:
Это позволяет использовать множественный выбор в сложных приложениях с управлением состоянием и серверными данными.
ToggleGroup использует:
Алгоритм:
onValueChangeЭто обеспечивает:
Если выбранный элемент исчезает из списка:
value больше не существует
React автоматически исключает его из интерфейса, но состояние необходимо синхронизировать.
Рекомендуется фильтрация:
setSelected(prev =>
prev.filter(v => available.includes(v))
);
Пример с HTML формой:
<input type="hidden" name="filters" value={selected.join(",")} />
Это позволяет отправить выбранные значения на сервер.
Часто используется хранение в URL.
Пример:
?filters=react,vue
Парсинг:
const filters = searchParams.get("filters").split(",");
При разработке сложных систем применяются:
Это позволяет использовать множественный выбор даже для десятков тысяч элементов.