React Aria предоставляет набор хуков и утилит для управления доступностью компонентов, сохраняя их контролируемыми или неконтролируемыми. Важно понимать, как состояние компонентов связано с UI и как правильно его синхронизировать с внутренним состоянием Aria для обеспечения корректной работы доступности.
Контролируемый компонент получает значение и функции обновления из внешнего состояния. Например, для поля ввода:
import { useTextField } from "@react-aria/textfield";
import { useState } from "react";
function ControlledInput() {
const [value, setValue] = useState("");
const { inputProps } = useTextField({
label: "Имя",
value,
onChange: setValue
});
return <input {...inputProps} />;
}
Ключевые моменты:
value полностью управляется внешним состоянием.onChange синхронизирует Aria с React состоянием.Неконтролируемый компонент хранит своё внутреннее состояние. Он удобен, когда внешний контроль не нужен:
import { useTextField } from "@react-aria/textfield";
function UncontrolledInput() {
const { inputProps } = useTextField({
label: "Имя",
defaultValue: "Иван"
});
return <input {...inputProps} />;
}
Особенности:
defaultValue задаёт начальное состояние.Для сложных компонентов, таких как Select,
ComboBox, CheckboxGroup, React Aria
использует хуки состояния из react-stately. Например:
import { useComboBoxState } from "@react-stately/combobox";
import { useComboBox } from "@react-aria/combobox";
function FruitSelector({ fruits }) {
const state = useComboBoxState({ items: fruits });
const { inputProps, listBoxProps, labelProps } = useComboBox({}, state);
return (
<div>
<label {...labelProps}>Выберите фрукт</label>
<input {...inputProps} />
{state.isOpen && (
<ul {...listBoxProps}>
{state.collection.map(item => (
<li key={item.key}>{item.rendered}</li>
))}
</ul>
)}
</div>
);
}
Здесь:
useComboBoxState управляет внутренним состоянием
выбора.useComboBox обеспечивает синхронизацию ARIA атрибутов с
этим состоянием.state.isOpen управляет видимостью выпадающего
списка.state.collection обеспечивает актуальные элементы для
UI и доступности.React Aria разделяет визуальное состояние и
логическое состояние для доступности. Хуки
useFocusRing, useHover и
useSelection позволяют синхронизировать UI и ARIA
атрибуты.
import { useFocusRing } from "@react-aria/focus";
import { useState } from "react";
function FocusableButton() {
const [isPressed, setPressed] = useState(false);
const { focusProps, isFocusVisible } = useFocusRing();
return (
<button
{...focusProps}
onMouseD own={() => setPressed(true)}
onMous eUp={() => setPressed(false)}
style={{
outline: isFocusVisible ? "2px solid blue" : "none",
background: isPressed ? "lightgray" : "white"
}}
>
Нажми меня
</button>
);
}
Особенности:
isFocusVisible синхронизирует визуальную подсветку
фокуса с состоянием.isPressed отражает состояние
нажатия.aria-pressed,
aria-selected) могут быть привязаны к этим состояниям для
поддержки вспомогательных технологий.Компоненты часто зависят от данных, получаемых асинхронно. React Aria позволяет синхронизировать состояние с внешними источниками без потери доступности:
import { useComboBoxState } from "@react-stately/combobox";
import { useComboBox } from "@react-aria/combobox";
import { useEffect, useState } from "react";
function AsyncFruitSelector() {
const [fruits, setFruits] = useState([]);
const state = useComboBoxState({ items: fruits });
const { inputProps, listBoxProps, labelProps } = useComboBox({}, state);
useEffect(() => {
fetch("/api/fruits")
.then(res => res.json())
.then(data => setFruits(data));
}, []);
return (
<div>
<label {...labelProps}>Выберите фрукт</label>
<input {...inputProps} />
{state.isOpen && (
<ul {...listBoxProps}>
{state.collection.map(item => (
<li key={item.key}>{item.rendered}</li>
))}
</ul>
)}
</div>
);
}
fruits автоматически обновляет
state.collection.react-stately, а отображение —
в компонентах с react-aria.useFocusRing и
useHover для точной синхронизации визуальных и
ARIA состояний.react-stately, чтобы компоненты оставались доступными и
корректными при динамическом рендеринге.Синхронизация состояния в React Aria строится вокруг идеи:
любое изменение данных должно отражаться одновременно в UI и
ARIA атрибутах. Хуки Aria обеспечивают корректное поведение
компонентов для пользователей с ограниченными возможностями, а
react-stately управляет внутренней логикой состояния,
включая выбор, открытие списков, фокус, выделение и асинхронные
обновления. Такой подход позволяет создавать доступные,
реактивные и легко масштабируемые интерфейсы.