React Aria предоставляет набор хуков и утилит для создания доступных компонентов в React. При работе с Next.js важно учитывать особенности серверного рендеринга (SSR), динамической загрузки компонентов и управления состоянием, чтобы элементы оставались доступными и корректно рендерились на сервере.
Next.js выполняет рендеринг на сервере, что накладывает ограничения на использование некоторых браузерных API, которые React Aria может применять внутри своих хуков. Например:
window, document и navigator
недоступны на сервере.useOverlayPosition, useFocusRing) требуют
DOM.Для корректной работы React Aria следует:
import { useEffect, useState } from 'react';
import { useButton } from '@react-aria/button';
export default function SSRSafeButton(props) {
const [isClient, setIsClient] = useState(false);
useEffect(() => {
setIsClient(true);
}, []);
if (!isClient) return <button {...props}>Загрузка...</button>;
let { buttonProps } = useButton(props);
return <button {...buttonProps}>{props.children}</button>;
}
import dynamic from 'next/dynamic';
const ClientOnlyComponent = dynamic(
() => import('../components/ClientOnlyComponent'),
{ ssr: false }
);
Это позволяет загружать компоненты, использующие React Aria, исключительно на клиенте, избегая ошибок при серверном рендеринге.
React Aria активно использует хуки для управления фокусом
(useFocusRing, useFocus) и клавиатурной
навигацией (useKeyboard, useListBox). При
интеграции с Next.js важно учитывать:
aria-*), чтобы начальная страница
была семантически корректной.Пример использования useListBox в Next.js:
import { useListBox, useListBoxSection } from '@react-aria/listbox';
import { useListState } from '@react-stately/list';
import { useEffect, useState } from 'react';
export default function ListBox({ items }) {
const [isClient, setIsClient] = useState(false);
useEffect(() => setIsClient(true), []);
const state = useListState({ items });
let ref = React.useRef();
let { listBoxProps } = useListBox({ 'aria-label': 'Выбор элементов' }, state, ref);
if (!isClient) return <ul>{items.map(item => <li key={item.key}>{item.name}</li>)}</ul>;
return (
<ul {...listBoxProps} ref={ref}>
{state.collection.map(item => (
<li key={item.key}>{item.rendered}</li>
))}
</ul>
);
}
Компоненты типа Dialog, Tooltip или
Menu используют React Aria Overlay, который рассчитывает
позиции относительно DOM. При серверном рендеринге позиционирование
невозможно, поэтому:
document перед вызовом хуков
useOverlayPosition.import { useOverlayPosition, useOverlayTrigger } from '@react-aria/overlays';
function PopoverTrigger() {
const [isOpen, setIsOpen] = useState(false);
const triggerRef = useRef();
const popoverRef = useRef();
const { triggerProps, overlayProps } = useOverlayTrigger(
{ type: 'dialog', isOpen, onOpenChange: setIsOpen },
triggerRef
);
const { overlayProps: positionProps } = useOverlayPosition({
targetRef: triggerRef,
overlayRef: popoverRef
});
return (
<>
<button {...triggerProps} ref={triggerRef}>Открыть поповер</button>
{isOpen && (
<div {...overlayProps} {...positionProps} ref={popoverRef}>
Содержимое Popover
</div>
)}
</>
);
}
aria-*Next.js позволяет формировать серверный HTML, поэтому важно, чтобы
компоненты уже имели базовые aria-* атрибуты. React Aria
автоматически добавляет их при использовании своих хуков, что улучшает
совместимость:
aria-expanded, aria-haspopup для
кнопок-триггеров.aria-selected, aria-disabled для списков и
элементов управления.role и aria-label для интерактивных
областей.При интеграции React Aria с Next.js следует учитывать производительность:
dynamic с { ssr: false }
только для действительно требующих DOM компонентов.React Aria полностью поддерживает TypeScript, и при работе с Next.js рекомендуется:
useListState и useSelectState.ref для Overlay и других
DOM-зависимых компонентов.React.RefObject и проверять наличие DOM
перед вызовом хуков, чтобы избежать ошибок при SSR.const triggerRef = useRef<HTMLButtonElement>(null);
const overlayRef = useRef<HTMLDivElement>(null);
Это обеспечивает строгую типизацию и предотвращает runtime ошибки на сервере.
aria-* для
доступности и SEO.