React Aria предоставляет набор хуков и утилит для создания доступных компонентов пользовательского интерфейса, при этом особое внимание уделяется синхронизации состояния между компонентами и их внутренними логическими структурами. Синхронизация состояния — это процесс обеспечения согласованного поведения компонентов при изменении данных, пользовательских действий или внешних событий.
В React Aria различают два типа состояния: контролируемое и неконтролируемое. Контролируемое состояние полностью управляется родительским компонентом через пропсы, тогда как неконтролируемое состояние хранится внутри самого компонента и обновляется при взаимодействии пользователя. Многие хуки React Aria позволяют работать с обоими типами состояния одновременно, обеспечивая гибкость и согласованность интерфейса.
useToggleState — один из базовых хуков для управления логическим состоянием (вкл/выкл). Его ключевые возможности:
import { useToggleState } from "@react-stately/toggle";
function CheckboxExample() {
let state = useToggleState({ defaultSelected: false });
return (
<input
type="checkbox"
checked={state.isSelected}
onCha nge={state.toggle}
/>
);
}
isSelected — текущее состояние элемента.toggle() — функция для переключения состояния.selected и defaultSelected.useListState — управляет состоянием списков,
например для ListBox или Select. Основные
свойства:
selectedKeys — множество выбранных элементов.disabledKeys — множество заблокированных
элементов.setSelectedKeys(keys) — изменение выбранных
элементов.Пример синхронизации состояния списка с внешним контролем:
import { useListState } from "@react-stately/list";
function SelectExample({ items, selectedKey, onSelectionChange }) {
let state = useListState({
items,
selectedKeys: selectedKey
});
return (
<ul>
{items.map(item => (
<li
key={item.key}
onCl ick={() => onSelectionChange(item.key)}
aria-selected={state.selectedKeys.has(item.key)}
>
{item.name}
</li>
))}
</ul>
);
}
Этот подход позволяет поддерживать синхронизацию состояния списка с внешним источником данных, сохраняя при этом полную доступность.
React Aria отделяет визуальное состояние и логическое состояние компонента. Для управления фокусом используются хуки:
onFocus и onBlur с возможностью управления
состоянием.Пример интеграции состояния фокуса с контролируемым состоянием:
import { useFocusRing } from "@react-aria/focus";
function Button({ onPress }) {
let { isFocusVisible, focusProps } = useFocusRing();
return (
<button {...focusProps} onCl ick={onPress} className={isFocusVisible ? 'focus-visible' : ''}>
Нажми меня
</button>
);
}
Такое разделение позволяет визуально реагировать на состояние без вмешательства в основную логику компонента.
Компоненты, такие как Combobox или Tabs,
требуют синхронизации нескольких состояний одновременно: выделение,
фокус, открытие меню и фильтрация данных. Для этого используются
комбинации хуков:
Пример синхронизации состояния вкладок:
import { useTabListState } from "@react-stately/tabs";
function TabsExample({ tabs }) {
let state = useTabListState({ selectedKey: 'tab1', defaultSelectedKey: 'tab1', orientation: 'horizontal' });
return (
<div role="tablist">
{tabs.map(tab => (
<button
role="tab"
key={tab.key}
aria-selected={state.selectedKey === tab.key}
onCl ick={() => state.setSelectedKey(tab.key)}
>
{tab.name}
</button>
))}
</div>
);
}
Здесь состояние выбранной вкладки можно контролировать извне через
пропс selectedKey или использовать внутреннее значение
defaultSelectedKey.
Для синхронизации состояния с внешними эффектами применяются хуки useEffect и useCallback, но React Aria добавляет утилиты для безопасной работы с асинхронными изменениями состояния:
Пример применения асинхронного списка:
import { useAsyncList } from "@react-stately/data";
function AsyncUserList({ fetchUsers }) {
let list = useAsyncList({
async load({ signal }) {
let users = await fetchUsers(signal);
return { items: users };
}
});
return (
<ul>
{list.items.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
);
}
Такой подход обеспечивает синхронизацию состояния даже при изменении данных на сервере или других внешних источниках.
useAsyncList или useControlledState, чтобы
избежать расхождений между UI и внутренним состоянием.useComboBoxState,
useTabListState) для сложных компонентов.Соблюдение этих принципов обеспечивает предсказуемое и доступное поведение компонентов при любых изменениях данных, действий пользователя или внешних событий.