Условные стили на основе состояния

В React Aria условные стили позволяют динамически изменять визуальное оформление компонентов в зависимости от их состояния: фокуса, выбора, наведения, активности и других интерактивных состояний. Основной принцип работы заключается в использовании предоставляемых библиотекой хуков, которые возвращают объект state и объект props, включающий атрибуты доступности и события, а также информацию о текущем состоянии компонента.


Хуки состояния и их связь со стилями

useButton

Хук useButton позволяет управлять кнопками с учётом доступности:

import { useButton } from '@react-aria/button';
import { useRef } from 'react';

function MyButton({ children, onPress }) {
  let ref = useRef();
  let { buttonProps, isPressed } = useButton({ onPress }, ref);

  return (
    <button
      {...buttonProps}
      ref={ref}
      className={isPressed ? 'button-pressed' : 'button-default'}
    >
      {children}
    </button>
  );
}
  • buttonProps — объект, содержащий все необходимые атрибуты для кнопки, включая aria-* свойства и обработчики событий.
  • isPressed — состояние нажатия, которое удобно использовать для применения условных стилей.

useCheckbox

Хук useCheckbox обеспечивает управление состоянием галочек с поддержкой доступности:

import { useCheckbox } from '@react-aria/checkbox';
import { useRef, useState } from 'react';

function MyCheckbox() {
  let [isSelected, setSelected] = useState(false);
  let ref = useRef();
  let { inputProps } = useCheckbox({ isSelected, onChange: setSelected }, ref);

  return (
    <label className={isSelected ? 'checkbox-checked' : 'checkbox-unchecked'}>
      <input {...inputProps} ref={ref} />
      Подписка
    </label>
  );
}
  • isSelected можно использовать для изменения классов CSS, цвета фона или отображения иконки состояния.

useToggleButton

Хук useToggleButton создаёт кнопку с переключаемым состоянием:

import { useToggleButton } from '@react-aria/button';
import { useRef, useState } from 'react';

function ToggleFavorite() {
  let [isSelected, setSelected] = useState(false);
  let ref = useRef();
  let { buttonProps } = useToggleButton({ isSelected, onChange: setSelected }, ref);

  return (
    <button
      {...buttonProps}
      ref={ref}
      className={isSelected ? 'favorite-active' : 'favorite-inactive'}
    >
      {isSelected ? '★' : '☆'}
    </button>
  );
}
  • Переключение стиля зависит от состояния isSelected, что позволяет менять визуальные индикаторы без сложной логики.

Использование mergeProps для условных классов

В сложных компонентах часто нужно объединять несколько наборов атрибутов и классов. React Aria предоставляет mergeProps для безопасного комбинирования обработчиков и атрибутов:

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

function AdvancedButton({ onPress }) {
  let ref = useRef();
  let { buttonProps, isPressed } = useButton({ onPress }, ref);

  let combinedProps = mergeProps(buttonProps, {
    className: isPressed ? 'btn-pressed' : 'btn-default'
  });

  return <button {...combinedProps} ref={ref}>Нажми меня</button>;
}
  • Позволяет объединять состояния и атрибуты без потери доступности.
  • Упрощает применение условных классов для сложных интерактивных компонентов.

Работа с useFocus и useHover для динамических стилей

useFocus

Хук useFocus предоставляет состояние фокуса и обработчики:

import { useFocus } from '@react-aria/interactions';
import { useRef, useState } from 'react';

function FocusInput() {
  let ref = useRef();
  let [isFocused, setFocused] = useState(false);
  let { focusProps } = useFocus({
    onFocusChange: setFocused
  });

  return (
    <input
      {...focusProps}
      ref={ref}
      className={isFocused ? 'input-focused' : 'input-blur'}
    />
  );
}
  • isFocused можно использовать для изменения бордера, тени или других визуальных индикаторов.

useHover

Хук useHover отслеживает наведение мыши:

import { useHover } from '@react-aria/interactions';
import { useRef, useState } from 'react';

function HoverableBox() {
  let ref = useRef();
  let [isHovered, setHovered] = useState(false);
  let { hoverProps } = useHover({
    onHoverChange: setHovered
  });

  return (
    <div
      {...hoverProps}
      ref={ref}
      className={isHovered ? 'box-hovered' : 'box-default'}
    >
      Наведи на меня
    </div>
  );
}
  • Состояние isHovered позволяет добавлять анимации, тени или изменять фон без вмешательства в структуру компонента.

Динамическая интеграция нескольких состояний

Комбинируя useFocus, useHover и useButton, можно создавать компоненты с комплексными визуальными состояниями:

import { useButton } from '@react-aria/button';
import { useFocus, useHover } from '@react-aria/interactions';
import { mergeProps } from '@react-aria/utils';
import { useRef, useState } from 'react';

function InteractiveButton({ onPress }) {
  let ref = useRef();
  let { buttonProps, isPressed } = useButton({ onPress }, ref);
  let [isFocused, setFocused] = useState(false);
  let [isHovered, setHovered] = useState(false);

  let { focusProps } = useFocus({ onFocusChange: setFocused });
  let { hoverProps } = useHover({ onHoverChange: setHovered });

  let combinedProps = mergeProps(buttonProps, focusProps, hoverProps, {
    className: isPressed
      ? 'btn-pressed'
      : isHovered
      ? 'btn-hovered'
      : isFocused
      ? 'btn-focused'
      : 'btn-default'
  });

  return <button {...combinedProps} ref={ref}>Кнопка</button>;
}
  • Позволяет управлять любым сочетанием состояний и адаптировать визуальное оформление под каждое.
  • Упрощает поддержку доступности и унифицированных интерактивных эффектов.

Принципы условного стайлинга

  1. Использовать состояния React Aria напрямуюisPressed, isSelected, isFocused, isHovered.
  2. Избегать дублирования событий — объединять атрибуты через mergeProps.
  3. Создавать читаемые классы CSS — например, button-pressed, button-hovered.
  4. Минимизировать inline-стили — предпочтительно управлять стилями через CSS или CSS-in-JS с условными классами.
  5. Поддерживать доступность — любые визуальные изменения должны сопровождаться корректными aria-* атрибутами, предоставляемыми хуками.

Такой подход обеспечивает универсальность, упрощает поддержку сложных компонентов и делает визуальные состояния интуитивно понятными как для разработчиков, так и для пользователей с различными потребностями.