Radix UI предоставляет низкоуровневые, полностью управляемые компоненты пользовательского интерфейса, где контроль состояния лежит на разработчике. Управление состоянием здесь критически важно для обеспечения синхронной работы UI и предсказуемого поведения компонентов.
Radix UI использует две модели управления состоянием:
onValueChange. Пример
использования:import * as Toggle from '@radix-ui/react-toggle';
<Toggle.Root defaultPressed={false} onPressedCha nge={(pressed) => console.log(pressed)}>
Toggle Me
</Toggle.Root>
Здесь defaultPressed задает начальное состояние, а
onPressedChange позволяет реагировать на изменения.
value или
checked. В этом случае компонент не хранит внутреннее
состояние, а лишь визуально отражает переданное значение:import * as Toggle from '@radix-ui/react-toggle';
import { useState } from 'react';
function ControlledToggle() {
const [pressed, setPressed] = useState(false);
return (
<Toggle.Root pressed={pressed} onPressedCha nge={setPressed}>
Toggle Me
</Toggle.Root>
);
}
Контролируемые компоненты позволяют интегрировать Radix UI с глобальным состоянием приложения, например Redux или Zustand.
Для таких компонентов, как Accordion, Tabs и CheckboxGroup, Radix UI поддерживает множественное и единичное состояние.
type="single" или
type="multiple" и управлять через value и
onValueChange:import * as Accordion from '@radix-ui/react-accordion';
import { useState } from 'react';
function ControlledAccordion() {
const [openItems, setOpenItems] = useState(['item1']);
return (
<Accordion.Root type="multiple" value={openItems} onValueCha nge={setOpenItems}>
<Accordion.Item value="item1">
<Accordion.Header>Item 1</Accordion.Header>
<Accordion.Content>Content 1</Accordion.Content>
</Accordion.Item>
<Accordion.Item value="item2">
<Accordion.Header>Item 2</Accordion.Header>
<Accordion.Content>Content 2</Accordion.Content>
</Accordion.Item>
</Accordion.Root>
);
}
import * as Checkbox from '@radix-ui/react-checkbox';
import { useState } from 'react';
function CheckboxGroupControlled() {
const [selected, setSelected] = useState(['apple']);
return (
<Checkbox.Group value={selected} onValueCha nge={setSelected}>
<Checkbox value="apple">Apple</Checkbox>
<Checkbox value="orange">Orange</Checkbox>
</Checkbox.Group>
);
}
Radix UI использует паттерн controlled component с callback-функциями, что обеспечивает двустороннюю синхронизацию состояния:
onValueChange или onPressedChange
вызываются при любом изменении состояния.value
или pressed, обеспечивая согласованность между UI и
данными.Для компонентов с анимацией (например, Dialog или
Popover) важно различать внешнее состояние
открытия (open) и внутреннее состояние
анимации (open + motion). Radix UI
автоматически управляет фазами анимации через свои элементы, но внешнее
состояние должно синхронизироваться с callback
onOpenChange:
import * as Dialog from '@radix-ui/react-dialog';
import { useState } from 'react';
function ControlledDialog() {
const [open, setOpen] = useState(false);
return (
<Dialog.Root open={open} onOpenCha nge={setOpen}>
<Dialog.Trigger>Open Dialog</Dialog.Trigger>
<Dialog.Content>
<Dialog.Close>Close</Dialog.Close>
</Dialog.Content>
</Dialog.Root>
);
}
Для сложного управления состоянием, когда несколько компонентов должны реагировать на одни и те же события, удобно использовать кастомные хуки:
import { useState } from 'react';
import * as Tabs from '@radix-ui/react-tabs';
function useTabsState(initialValue) {
const [value, setValue] = useState(initialValue);
const selectTab = (tabValue) => setValue(tabValue);
return { value, selectTab };
}
function TabsExample() {
const tabs = useTabsState('tab1');
return (
<Tabs.Root value={tabs.value} onValueCha nge={tabs.selectTab}>
<Tabs.List>
<Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
<Tabs.Trigger value="tab2">Tab 2</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="tab1">Content 1</Tabs.Content>
<Tabs.Content value="tab2">Content 2</Tabs.Content>
</Tabs.Root>
);
}
Такой подход упрощает масштабирование и переиспользование логики управления состоянием.
Эти принципы делают работу с Radix UI гибкой и предсказуемой, обеспечивая точный контроль над состоянием интерфейса.