Хуки useFloating

Хук useFloating является ядром библиотеки Floating UI для создания плавающих элементов, таких как тултипы, поповеры, дропдауны и модальные окна. Его основная задача — динамически рассчитывать позицию элемента относительно другого, учитывая размеры, ограничения видимой области и возможности смещения.

import { useFloating } from '@floating-ui/react-dom';

Хук возвращает объект с ключевыми свойствами и методами: x, y, strategy, refs и вспомогательные функции для обновления позиции.

  • x и y — координаты плавающего элемента относительно контейнера.
  • strategy — стратегия позиционирования: "absolute" или "fixed".
  • refs — объект, содержащий ссылки на целевой элемент (reference) и на сам плавающий элемент (floating).
  • update() — функция для пересчета позиции при изменении размеров или состояния DOM.
const { x, y, reference, floating, strategy, update } = useFloating({
  placement: 'bottom-start',
  middleware: [],
});

Параметры хука useFloating

  • placement — определяет начальную позицию плавающего элемента относительно целевого: "top", "bottom", "left", "right" и их вариации с "-start" и "-end".
  • strategy — выбирается "absolute" (позиционирование внутри ближайшего родителя с position: relative) или "fixed" (позиционирование относительно окна).
  • middleware — массив функций, влияющих на вычисление позиции, например offset, flip, shift, arrow.
  • open и onOpenChange — используются для управления видимостью элемента, интеграция с кастомными состояниями.

Пример с базовыми middleware:

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

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

Middleware: детальное использование

offset Добавляет смещение от целевого элемента. Может принимать число или функцию:

offset(8); // смещение 8px
offset(({ rects }) => rects.reference.height / 2); // динамическое смещение

flip Позволяет менять позицию при нехватке пространства. Можно настроить через fallbackPlacements:

flip({
  fallbackPlacements: ['bottom', 'right'],
});

shift Корректирует позицию элемента, чтобы он не выходил за границы контейнера или viewport:

shift({
  padding: 10, // минимальный отступ от краев
  limiter: undefined, // можно использовать custom limiter
});

arrow Используется для позиционирования стрелки относительно целевого элемента:

import { arrow } from '@floating-ui/react-dom';

const arrowRef = useRef(null);

arrow({ element: arrowRef.current });

arrow автоматически возвращает координаты для позиционирования стрелки: x и y.


Связь refs с DOM

Хук предоставляет объект refs, который содержит два ключевых свойства:

  • reference — ссылка на элемент, относительно которого позиционируется поповер.
  • floating — ссылка на сам плавающий элемент.

Пример использования в компоненте React:

<button ref={reference}>Открыть меню</button>
<div
  ref={floating}
  style={{
    position: strategy,
    top: y ?? 0,
    left: x ?? 0,
  }}
>
  Меню
</div>

refs автоматически обрабатывает пересчет позиции при изменении DOM, если используется autoUpdate.


Автоматическое обновление позиции

Функция autoUpdate позволяет динамически реагировать на изменения размеров или прокрутки:

import { autoUpdate } from '@floating-ui/react-dom';

useEffect(() => {
  if (reference.current && floating.current) {
    return autoUpdate(reference.current, floating.current, update);
  }
}, [reference, floating, update]);

autoUpdate слушает события resize, scroll и любые изменения размеров элементов.


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

Часто useFloating используется вместе с контролем видимости:

const [open, setOpen] = useState(false);
const { x, y, reference, floating, strategy } = useFloating({
  placement: 'bottom',
  open,
  onOpenChange: setOpen,
});

Теперь можно использовать open для условного рендеринга и onOpenChange для синхронизации с внешним состоянием.


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

useFloating легко комбинируется с useClick, useHover и другими событиями:

import { useHover, useInteractions } from '@floating-ui/react-dom-interactions';

const hover = useHover(context);
const interactions = useInteractions([hover]);

<button ref={interactions.getReferenceProps(reference)}>Наведи</button>
<div ref={interactions.getFloatingProps(floating)}>Контент</div>

useInteractions автоматически обрабатывает все необходимые события и синхронизирует открытие/закрытие.


Практические рекомендации

  • Всегда использовать middleware для гибкого позиционирования.
  • Предпочтительно применять autoUpdate при динамическом контенте.
  • Для стрелок использовать отдельный ref и middleware arrow.
  • Сочетание с useInteractions упрощает управление событиями мыши и клавиатуры.
  • Стратегия "fixed" полезна для модальных окон и элементов, которые должны оставаться на экране при скролле.

Эти возможности делают useFloating мощным инструментом для построения адаптивных, интерактивных интерфейсов с точным контролем позиционирования и поведением плавающих элементов.