React и hooks

Radix UI предоставляет низкоуровневые, доступные компоненты для React, которые легко интегрируются с современными подходами разработки через Hooks. Библиотека ориентирована на контроль состояния компонентов и их взаимодействие с DOM, предоставляя готовые primitives, которые можно кастомизировать с помощью стилей и логики React.


Управление состоянием через Hooks

В Radix UI практически каждый компонент предоставляет собственный механизм управления состоянием. Например, для компонентов Dialog, Popover, Accordion или Tabs можно использовать встроенные состояния, но часто используется подход через useState или useReducer для более сложной логики:

import * as Dialog from '@radix-ui/react-dialog';
import { useState } from 'react';

function Example() {
  const [open, setOpen] = useState(false);

  return (
    <Dialog.Root open={open} onOpenCha nge={setOpen}>
      <Dialog.Trigger>Открыть диалог</Dialog.Trigger>
      <Dialog.Content>
        <Dialog.Title>Заголовок</Dialog.Title>
        <Dialog.Description>Описание диалога</Dialog.Description>
        <Dialog.Close>Закрыть</Dialog.Close>
      </Dialog.Content>
    </Dialog.Root>
  );
}

Ключевые моменты:

  • open и onOpenChange позволяют полностью контролировать состояние.
  • Trigger автоматически связывается с Root и инициирует открытие или закрытие.
  • Возможность синхронизации состояния с внешними данными через Hooks делает компоненты гибкими.

Использование useId и других вспомогательных Hooks

Для создания уникальных идентификаторов, необходимых для aria атрибутов, Radix рекомендует использовать useId:

import { useId } from '@radix-ui/react-id';

function CheckboxExample() {
  const id = useId();

  return (
    <>
      <label htmlFor={id}>Подтвердить условия</label>
      <input type="checkbox" id={id} />
    </>
  );
}

Преимущества useId:

  • Автоматическая генерация уникальных идентификаторов.
  • Поддержка серверного рендеринга в React.
  • Упрощает связывание элементов с доступными aria атрибутами.

Комбинирование Radix UI с пользовательскими Hooks

Radix UI компоненты легко интегрируются с собственными React Hooks для реализации сложной логики. Например, объединение состояния Accordion с локальным хранилищем:

import * as Accordion from '@radix-ui/react-accordion';
import { useState, useEffect } from 'react';

function PersistentAccordion() {
  const [value, setValue] = useState(() => localStorage.getItem('accordion') || 'item1');

  useEffect(() => {
    localStorage.setItem('accordion', value);
  }, [value]);

  return (
    <Accordion.Root type="single" value={value} onValueCha nge={setValue}>
      <Accordion.Item value="item1">
        <Accordion.Header>
          <Accordion.Trigger>Секция 1</Accordion.Trigger>
        </Accordion.Header>
        <Accordion.Content>Контент 1</Accordion.Content>
      </Accordion.Item>
      <Accordion.Item value="item2">
        <Accordion.Header>
          <Accordion.Trigger>Секция 2</Accordion.Trigger>
        </Accordion.Header>
        <Accordion.Content>Контент 2</Accordion.Content>
      </Accordion.Item>
    </Accordion.Root>
  );
}

Особенности такого подхода:

  • Хранение состояния вне DOM компонентов позволяет синхронизировать интерфейс с внешними источниками данных.
  • Использование onValueChange в сочетании с useEffect обеспечивает реактивное обновление состояния.
  • Поддержка single и multiple типов Accordion делает компонент универсальным.

Контролируемые и неконтролируемые компоненты

Radix UI позволяет использовать компоненты в двух режимах:

  1. Контролируемый компонент — состояние полностью управляется через React Hooks (useState, useReducer).
  2. Неконтролируемый компонент — внутреннее состояние управляется самим компонентом, можно получать события через колбэки.

Пример неконтролируемого Popover:

import * as Popover from '@radix-ui/react-popover';

function UncontrolledPopover() {
  return (
    <Popover.Root>
      <Popover.Trigger>Открыть Popover</Popover.Trigger>
      <Popover.Content>
        <p>Неконтролируемый контент</p>
        <Popover.Close>Закрыть</Popover.Close>
      </Popover.Content>
    </Popover.Root>
  );
}

Анимации и transition с использованием Hooks

Radix UI компоненты не навязывают конкретную библиотеку для анимаций, поэтому анимации можно интегрировать через useState и CSS transition:

import * as Toast from '@radix-ui/react-toast';
import { useState } from 'react';

function AnimatedToast() {
  const [open, setOpen] = useState(false);

  return (
    <>
      <button onCl ick={() => setOpen(true)}>Показать уведомление</button>
      <Toast.Root open={open} onOpenCha nge={setOpen} className="transition-all duration-300">
        <Toast.Title>Успешно</Toast.Title>
        <Toast.Description>Действие выполнено</Toast.Description>
        <Toast.Close>Закрыть</Toast.Close>
      </Toast.Root>
    </>
  );
}

Рекомендации по анимациям:

  • Управление состоянием через Hooks позволяет плавно контролировать открытие и закрытие.
  • CSS transition или framer-motion можно подключать напрямую к Radix компонентам для кастомного поведения.

Контекст и useContext с Radix UI

Radix UI компоненты удобно использовать внутри React Context для передачи глобальных настроек. Например, для темы или локализации:

import * as Tabs from '@radix-ui/react-tabs';
import { createContext, useContext, useState } from 'react';

const TabsContext = createContext();

function TabsProvider({ children }) {
  const [tab, setTab] = useState('tab1');
  return <TabsContext.Provider value={{ tab, setTab }}>{children}</TabsContext.Provider>;
}

function CustomTabs() {
  const { tab, setTab } = useContext(TabsContext);

  return (
    <Tabs.Root value={tab} onValueCha nge={setTab}>
      <Tabs.List>
        <Tabs.Trigger value="tab1">Вкладка 1</Tabs.Trigger>
        <Tabs.Trigger value="tab2">Вкладка 2</Tabs.Trigger>
      </Tabs.List>
      <Tabs.Content value="tab1">Контент 1</Tabs.Content>
      <Tabs.Content value="tab2">Контент 2</Tabs.Content>
    </Tabs.Root>
  );
}

Использование useContext позволяет:

  • Централизованно управлять состоянием для нескольких компонентов.
  • Создавать масштабируемые интерфейсы с Radix UI primitives.
  • Упрощать синхронизацию состояния между различными компонентами интерфейса.

Итоговые рекомендации по Hooks и Radix UI

  • Всегда использовать open и onOpenChange для контролируемых компонентов.
  • Для уникальных идентификаторов применять useId для совместимости с SSR.
  • Сохранять состояние компонентов вне Radix primitives через React Hooks для сложных интерфейсов.
  • Комбинировать с Context для глобальных настроек и синхронизации состояния.
  • Анимации и transitions лучше реализовывать через CSS или сторонние библиотеки, используя Hooks для управления состоянием.

Эти принципы делают использование Radix UI с React максимально гибким, доступным и предсказуемым, обеспечивая контроль над любым поведением компонента через Hooks.