Floating UI предоставляет низкоуровневые утилиты для позиционирования всплывающих элементов, таких как тултипы, поповеры и модальные окна. В контексте 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 вместе с состоянием React обеспечивает
точное и адаптивное позиционирование элементов, плавную анимацию
появления, реакцию на динамические изменения размеров и централизованное
управление видимостью. Комбинация useFloating, состояния
useState, useLayoutEffect и
update() делает интерфейс полностью реактивным и
предсказуемым, при этом сохраняя лёгкость и модульность кода.