Интеграция с React состоянием

Floating UI предоставляет низкоуровневые утилиты для позиционирования всплывающих элементов, таких как тултипы, поповеры и модальные окна. В контексте React важным аспектом является интеграция с состоянием компонентов для управления видимостью, размерами и позиционированием элементов в реальном времени.

Контроль видимости через состояние React

Основной способ управления видимостью всплывающих элементов — использование состояния useState. Например, для управления отображением тултипа:

import React, { useState } from 'react';
import { useFloating, offset, shift, flip } from '@floating-ui/react-dom';

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

  const { x, y, reference, floating, strategy } = useFloating({
    placement: 'top',
    middleware: [offset(10), flip(), shift({ padding: 5 })],
  });

  return (
    <>
      <button
        ref={reference}
        onMouseEn ter={() => setOpen(true)}
        onMouseLe ave={() => setOpen(false)}
      >
        Наведи на меня
      </button>
      {open && (
        <div
          ref={floating}
          style={{
            position: strategy,
            top: y ?? 0,
            left: x ?? 0,
          }}
        >
          Я тултип!
        </div>
      )}
    </>
  );
}

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

  • useFloating возвращает координаты x, y, стратегию позиционирования strategy, а также reference и floating рефы.
  • middleware позволяет автоматически корректировать позицию: смещение (offset), предотвращение выхода за границы (shift), переворот при нехватке места (flip).
  • Состояние open контролирует рендеринг элемента и его реакцию на события мыши.

Синхронизация с анимациями и плавным открытием

Для интеграции с анимациями лучше использовать сочетание состояния open и CSS-переходов. React позволяет плавно управлять классами:

<div
  ref={floating}
  style={{
    position: strategy,
    top: y ?? 0,
    left: x ?? 0,
    opacity: open ? 1 : 0,
    transition: 'opacity 0.2s ease-in-out',
    pointerEvents: open ? 'auto' : 'none',
  }}
>
  Анимированный тултип
</div>

Такой подход позволяет элементу появляться плавно, не создавая лишнего пересоздания DOM-дерева.

Управление размерами и динамическим содержимым

Floating UI позволяет учитывать размеры всплывающих элементов, которые могут изменяться динамически. В React это удобно через useLayoutEffect:

import { useLayoutEffect, useRef, useState } from 'react';

function DynamicTooltip({ content }) {
  const [open, setOpen] = useState(false);
  const floatingRef = useRef(null);
  const [size, setSize] = useState({ width: 0, height: 0 });

  useLayoutEffect(() => {
    if (floatingRef.current) {
      const rect = floatingRef.current.getBoundingClientRect();
      setSize({ width: rect.width, height: rect.height });
    }
  }, [content, open]);

  // Используем size для дополнительных вычислений, например offset
  ...
}

Особенности:

  • useLayoutEffect срабатывает синхронно после изменений DOM, что гарантирует точные размеры.
  • Можно адаптировать middleware Floating UI под текущие размеры контента, обеспечивая корректное позиционирование даже при изменении текста или компонентов внутри всплывающего окна.

Реактивное позиционирование при изменении состояния

Floating UI позволяет повторно вычислять позицию при изменении состояния React. Например, при переключении вкладок или обновлении контента:

const { x, y, reference, floating, update } = useFloating({
  placement: 'bottom',
});

useEffect(() => {
  if (open) {
    update(); // пересчитывает координаты при открытии
  }
}, [open, content]);
  • Метод update() пересчитывает координаты с учётом текущих размеров и позиции родительского элемента.
  • Это важно для реактивного интерфейса, где элементы могут динамически менять размеры или смещаться.

Интеграция с контекстами и глобальным состоянием

При использовании глобального состояния через Redux, Zustand или Context API можно управлять видимостью всплывающих элементов централизованно. Например:

const { tooltipOpen, setTooltipOpen } = useTooltipContext();

<button
  ref={reference}
  onMouseEn ter={() => setTooltipOpen(true)}
  onMouseLe ave={() => setTooltipOpen(false)}
>
  Кнопка с глобальным тултипом
</button>
  • Глобальное состояние позволяет синхронизировать несколько всплывающих элементов и управлять их логикой централизованно.
  • Floating UI остаётся инструментом расчета позиции и middleware, а React обеспечивает управление жизненным циклом и состоянием.

Вывод

Использование Floating UI вместе с состоянием React обеспечивает точное и адаптивное позиционирование элементов, плавную анимацию появления, реакцию на динамические изменения размеров и централизованное управление видимостью. Комбинация useFloating, состояния useState, useLayoutEffect и update() делает интерфейс полностью реактивным и предсказуемым, при этом сохраняя лёгкость и модульность кода.