useCalendar — это хук из библиотеки React Aria,
предназначенный для управления логикой календаря и обеспечения
доступности компонентов, отображающих даты. Он отделяет логику выбора
даты, управления фокусом и навигации от визуальной реализации, позволяя
создавать собственные UI-компоненты с поддержкой стандартов доступности
(ARIA).
Для использования useCalendar необходимо импортировать
соответствующие хуки из @react-aria/calendar и
@react-stately/calendar:
import { useCalendar } from '@react-aria/calendar';
import { useCalendarState } from '@react-stately/calendar';
import { createCalendar } from '@internationalized/date';
useCalendar тесно связан с состоянием календаря, которое
управляется через useCalendarState. Этот стейт хранит
выбранную дату, текущий фокус и логику навигации между месяцами и
годами.
const state = useCalendarState({
locale: 'ru-RU',
createCalendar,
visibleDuration: { months: 1 }
});
Пояснения параметров:
locale — локаль для отображения месяцев и дней
недели.createCalendar — функция из
@internationalized/date, создающая объект календаря.visibleDuration — определяет, сколько месяцев
отображается одновременно. Обычно это один месяц, но можно расширить на
несколько.useCalendar принимает объект со свойствами состояния и
возвращает атрибуты для контейнера календаря и отдельных дней:
const { calendarProps, prevButtonProps, nextButtonProps } = useCalendar({
'aria-label': 'Календарь',
state
});
calendarProps — атрибуты, которые нужно передать
корневому элементу календаря. Включают ARIA-атрибуты для
доступности.prevButtonProps и nextButtonProps —
свойства для кнопок навигации между месяцами.useCalendar обеспечивает корректную работу
клавиатуры:
Фокус всегда остаётся в пределах текущего месяца, что предотвращает потерю контекста пользователем с ограниченными возможностями.
Состояние календаря управляет выбранной датой через объект
state.selectedDate. Изменение даты осуществляется функцией
state.setSelectedDate(date). Пример интеграции с
пользовательским компонентом дня:
{state.visibleDays.map(day => (
<button
key={day.toString()}
{...state.getDayProps(day)}
className={state.isSelected(day) ? 'selected' : ''}
>
{day.day}
</button>
))}
state.visibleDays — массив объектов дат текущего
месяца.state.getDayProps(day) — возвращает необходимые
ARIA-атрибуты для дня.state.isSelected(day) — проверяет, выбрана ли
дата.useCalendarState поддерживает выбор диапазона и
ограничения:
const state = useCalendarState({
locale: 'ru-RU',
createCalendar,
minValue: new Date(2024, 0, 1),
maxValue: new Date(2024, 11, 31),
visibleDuration: { months: 1 }
});
minValue и maxValue ограничивают возможные
даты.useRangeCalendarState,
но интерфейс useCalendar и методы остаются
аналогичными.Для корректного отображения дней недели и месяцев в
useCalendar важно использовать locale и
createCalendar из @internationalized/date.
Например:
import { CalendarDate } from '@internationalized/date';
const today = new CalendarDate();
const formatted = today.toString(); // формат по локали
Основная сила useCalendar в том, что он не навязывает
визуальный вид. Можно создать сетку месяцев, стилизованные кнопки дней,
стрелки навигации и полностью кастомные элементы, передавая полученные
атрибуты:
<div {...calendarProps}>
<button {...prevButtonProps}>Назад</button>
<button {...nextButtonProps}>Вперед</button>
<div className="days-grid">
{state.visibleDays.map(day => (
<button key={day.toString()} {...state.getDayProps(day)}>
{day.day}
</button>
))}
</div>
</div>
При больших календарях или отображении нескольких месяцев:
visibleDuration для ограничения числа
рендеримых месяцев.React.memo.useCalendar легко интегрируется с:
useCalendarGrid — для работы с сеткой дней.useCalendarCell — для отдельного дня с учетом
ARIA.useLocale — для динамического изменения локали.useFocusRing — для визуальной индикации фокуса.Такое сочетание позволяет создавать полностью доступные, локализованные и кастомные календарные компоненты без потери гибкости и производительности.