React Aria — это библиотека, предоставляющая доступные компоненты и
хуки для работы с интерфейсами в React. Интеграция с TypeScript
позволяет добиться строгой типизации, повышения предсказуемости кода и
предотвращения распространённых ошибок на этапе компиляции. Важнейшими
аспектами являются типизация хуков, пропсов компонентов и работа с
DOM-элементами через ref.
Хуки React Aria обычно возвращают объект с набором атрибутов и обработчиков событий, которые следует применять к JSX-элементам. Для корректной работы в TypeScript необходимо указывать типы возвращаемых значений и целевых элементов.
Пример для useButton:
import {useButton} from '@react-aria/button';
import {ButtonHTMLAttributes, RefObject} from 'react';
interface CustomButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
label: string;
}
function CustomButton(props: CustomButtonProps) {
const ref: RefObject<HTMLButtonElement> = React.useRef(null);
const {buttonProps} = useButton(props, ref);
return <button {...buttonProps} ref={ref}>{props.label}</button>;
}
Здесь:
RefObject<HTMLButtonElement> гарантирует
корректный тип для DOM-элемента кнопки.CustomButtonProps расширяет стандартные
атрибуты кнопки HTML и добавляет необходимые пользовательские
свойства.useButton возвращает объект с buttonProps,
который корректно типизирован TypeScript и может быть безопасно
распространён на элемент.useTextField и другими интерактивными компонентамиДля полей ввода и текстовых областей важно учитывать типизацию
событий и атрибутов. React Aria использует универсальные типы для
событий KeyboardEvent и FocusEvent.
import {useTextField} from '@react-aria/textfield';
import {InputHTMLAttributes, RefObject} from 'react';
interface CustomTextFieldProps extends InputHTMLAttributes<HTMLInputElement> {
label: string;
}
function CustomTextField(props: CustomTextFieldProps) {
const ref: RefObject<HTMLInputElement> = React.useRef(null);
const {labelProps, inputProps} = useTextField(props, ref);
return (
<label {...labelProps}>
{props.label}
<input {...inputProps} ref={ref} />
</label>
);
}
Особенности:
labelProps и inputProps автоматически
включают все необходимые атрибуты для доступности.React Aria позволяет объединять несколько хуков для сложных компонентов, например, комбинирование фокуса, клавиатурной навигации и управления состоянием:
import {useButton} from '@react-aria/button';
import {useFocusRing} from '@react-aria/focus';
import {ButtonHTMLAttributes, RefObject} from 'react';
interface AccessibleButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
label: string;
}
function AccessibleButton(props: AccessibleButtonProps) {
const ref: RefObject<HTMLButtonElement> = React.useRef(null);
const {buttonProps} = useButton(props, ref);
const {isFocusVisible, focusProps} = useFocusRing();
return (
<button
{...buttonProps}
{...focusProps}
ref={ref}
style={{
outline: isFocusVisible ? '2px solid blue' : 'none'
}}
>
{props.label}
</button>
);
}
Преимущества:
buttonProps,
focusProps).Некоторые хуки React Aria, например useCheckbox или
useRadioGroup, требуют управления состоянием через объекты
состояния, которые можно типизировать.
import {useCheckbox} from '@react-aria/checkbox';
import {useToggleState} from '@react-stately/toggle';
import {RefObject} from 'react';
interface CheckboxProps {
label: string;
}
function CustomCheckbox({label}: CheckboxProps) {
const state = useToggleState();
const ref: RefObject<HTMLInputElement> = React.useRef(null);
const {inputProps} = useCheckbox({isSelected: state.isSelected, onChange: state.setSelected}, state, ref);
return (
<label>
<input {...inputProps} ref={ref} />
{label}
</label>
);
}
Особенности:
useToggleState предоставляет типизированный объект
состояния.isSelected и onChange строго
типизированы, что исключает неправильное присвоение значений.ref для
элементов, передаваемых в хуки.ButtonHTMLAttributes,
InputHTMLAttributes и т.д.).@react-stately, чтобы хуки получали корректно
типизированные объекты.React Aria позволяет строить полностью кастомные элементы с сохранением доступности. Важно типизировать все свойства, которые будут переданы в хук:
import {useMenu} from '@react-aria/menu';
import {AriaMenuProps} from '@react-types/menu';
import {RefObject} from 'react';
interface CustomMenuProps extends AriaMenuProps<HTMLElement> {
items: string[];
}
function CustomMenu({items, ...props}: CustomMenuProps) {
const ref: RefObject<HTMLUListElement> = React.useRef(null);
const {menuProps} = useMenu(props, ref);
return (
<ul {...menuProps} ref={ref}>
{items.map(item => <li key={item}>{item}</li>)}
</ul>
);
}
Типизация гарантирует:
AriaMenuProps.ref на HTML-элемент.Интеграция React Aria с TypeScript требует внимательного подхода к
типизации ref, пропсов компонентов и состояния. Строгая
типизация не только предотвращает ошибки, но и обеспечивает
автоматическую документацию кода, улучшает автодополнение в IDE и
повышает качество разработки доступных интерфейсов.