Создание кастомных хуков

Кастомные хуки позволяют инкапсулировать сложную логику взаимодействия с элементами интерфейса и переиспользовать её в разных компонентах. В контексте React Aria кастомные хуки часто создаются для управления состоянием, обработки событий и обеспечения доступности через ARIA-атрибуты.

Ключевое преимущество кастомного хука — возможность отделить логику от представления. В React Aria это особенно важно, так как библиотека предоставляет примитивы для доступности, но не диктует визуальное оформление.


Структура кастомного хука

Типичный кастомный хук в React Aria включает следующие элементы:

  1. Импорт необходимых утилит:
import { useButton } from '@react-aria/button';
import { useFocusRing } from '@react-aria/focus';
import { mergeProps } from '@react-aria/utils';
  1. Определение состояния:
import { useState } from 'react';

function useCustomToggle(initialState = false) {
  const [isToggled, setToggled] = useState(initialState);
  
  const toggle = () => setToggled(prev => !prev);
  
  return { isToggled, toggle };
}

Состояние управляет логикой поведения элемента, отделяя её от UI-компонента.

  1. Интеграция с React Aria: Использование готовых хуков библиотеки позволяет обеспечить ARIA-поддержку.
function useAccessibleButton(props) {
  const ref = React.useRef();
  const { buttonProps } = useButton(props, ref);
  const { isFocusVisible, focusProps } = useFocusRing();

  return {
    ref,
    props: mergeProps(buttonProps, focusProps),
    isFocusVisible
  };
}

Здесь mergeProps объединяет стандартные свойства кнопки с логикой фокуса, гарантируя корректное управление доступностью.


Пример комплексного кастомного хука

Создание toggle-кнопки с поддержкой клавиатуры и ARIA:

import { useButton } from '@react-aria/button';
import { useFocusRing } from '@react-aria/focus';
import { mergeProps } from '@react-aria/utils';
import { useState, useRef } from 'react';

function useToggleButton(initialState = false, onChange) {
  const [isSelected, setSelected] = useState(initialState);
  const ref = useRef();

  const handleToggle = () => {
    setSelected(prev => !prev);
    if (onChange) onChange(!isSelected);
  };

  const { buttonProps } = useButton({ onPress: handleToggle, 'aria-pressed': isSelected }, ref);
  const { isFocusVisible, focusProps } = useFocusRing();

  return {
    ref,
    props: mergeProps(buttonProps, focusProps),
    isSelected,
    isFocusVisible
  };
}

Использование хука в компоненте:

function ToggleButton() {
  const { ref, props, isSelected, isFocusVisible } = useToggleButton(false, (state) => console.log(state));

  return (
    <button ref={ref} {...props} style={{ border: isFocusVisible ? '2px solid blue' : '1px solid gray' }}>
      {isSelected ? 'Вкл' : 'Выкл'}
    </button>
  );
}

Особенности и рекомендации

  • Изоляция логики: кастомные хуки должны управлять исключительно состоянием и поведением, не вмешиваясь в рендер.
  • Использование ARIA-хуков: useButton, useCheckbox, useMenu и другие позволяют автоматически генерировать корректные атрибуты.
  • Объединение свойств: mergeProps безопасно комбинирует несколько наборов свойств, предотвращая конфликты событий.
  • Поддержка фокуса: useFocusRing помогает визуализировать доступность через клавиатуру и поддерживает стандарты WCAG.
  • Переиспользуемость: один и тот же кастомный хук можно применять для разных компонентов с идентичной логикой, меняя только внешний вид.

Взаимодействие с состоянием и событиями

Кастомные хуки React Aria часто управляют сложными состояниями, например:

  • открытие/закрытие модального окна,
  • выбор элементов в списке,
  • переключение вкладок.

Пример: хук для управления состоянием меню:

import { useMenuTriggerState } from '@react-stately/menu';
import { useMenuTrigger } from '@react-aria/menu';

function useCustomMenu() {
  const state = useMenuTriggerState();
  const ref = useRef();
  const { menuTriggerProps, menuProps } = useMenuTrigger({}, state, ref);

  return { ref, state, menuTriggerProps, menuProps };
}

Такой подход разделяет логическое состояние меню (открыто/закрыто, выбранный элемент) и поведение интерфейса, что упрощает поддержку и тестирование.


Итоговая структура эффективного кастомного хука

  1. Импорт React и React Aria утилит.
  2. Создание локального состояния и функций-обработчиков.
  3. Применение ARIA-хуков для доступности.
  4. Объединение свойств через mergeProps.
  5. Возврат объекта с ref, props, состоянием и флагами фокуса.

Такой подход гарантирует чистый, переиспользуемый и доступный код, полностью совместимый с экосистемой React Aria.