Floating UI в JavaScript предоставляет гибкую систему позиционирования всплывающих элементов, таких как тултипы, модальные окна, дропдауны и поповеры. Для эффективного использования библиотеки крайне важно правильно управлять ресурсами и очисткой эффектов, чтобы избежать утечек памяти и некорректного поведения элементов на странице.
Когда создается floating-элемент с помощью
computePosition или хуков вроде useFloating,
библиотека добавляет обработчики событий на окно и документ для
отслеживания изменения размеров, скролла и положения целевого
элемента.
Пример создания позиционируемого элемента:
import { computePosition, offset, flip, shift } from '@floating-ui/dom';
const reference = document.querySelector('#button');
const floating = document.querySelector('#tooltip');
computePosition(reference, floating, {
middleware: [offset(8), flip(), shift()],
}).then(({ x, y }) => {
Object.assign(floating.style, {
left: `${x}px`,
top: `${y}px`,
});
});
После этого Floating UI регистрирует слушатели событий на скролл и ресайз. Если элемент будет удален из DOM без очистки, эти слушатели останутся, что приведет к утечкам памяти и ненужным вычислениям.
cleanup и возвращаемые функции от
autoUpdateДля упрощения управления ресурсами библиотека предоставляет утилиту
autoUpdate, которая автоматически обновляет положение
floating-элемента при изменениях DOM.
Использование с очисткой ресурсов:
import { autoUpdate } from '@floating-ui/dom';
const cleanup = autoUpdate(reference, floating, () => {
computePosition(reference, floating, {
middleware: [offset(8), flip(), shift()],
}).then(({ x, y }) => {
Object.assign(floating.style, {
left: `${x}px`,
top: `${y}px`,
});
});
});
// Когда элемент больше не нужен
cleanup();
autoUpdate —
функция, вызываемая для удаления всех подписок и обработчиков.cleanup() гарантирует полное освобождение
ресурсов: слушатели событий удаляются, вычисления прекращаются.Если floating-элемент удаляется динамически, необходимо убедиться, что очистка вызывается до удаления элемента:
button.addEventListener('click', () => {
floating.remove();
cleanup(); // удаляет слушатели и прекращает автообновление
});
Пренебрежение этим правилом приводит к dangling listeners, которые будут продолжать работать с несуществующими элементами и вызывать ошибки в консоли.
Middleware в Floating UI также могут создавать временные объекты, например, измерительные контейнеры или временные прокси для расчета размеров. Для некоторых middleware можно явно запускать методы очистки, особенно при использовании кастомных решений:
import { arrow } from '@floating-ui/dom';
const arrowMiddleware = arrow({ element: arrowEl });
// computePosition с middleware
const position = await computePosition(reference, floating, {
middleware: [offset(8), flip(), shift(), arrowMiddleware],
});
// При удалении
if (arrowMiddleware.cleanup) {
arrowMiddleware.cleanup();
}
Такой подход предотвращает накопление временных элементов в DOM и повышает производительность приложения.
В React используется пакет @floating-ui/react, где
управление жизненным циклом элементов интегрировано через хуки
useFloating и useInteractions.
Пример с автоматической очисткой:
import { useFloating } from '@floating-ui/react';
import { useEffect } from 'react';
function Tooltip({ referenceElement }) {
const { x, y, reference, floating, update } = useFloating({
placement: 'top',
});
useEffect(() => {
reference(referenceElement);
return () => {
// cleanup вызывается автоматически при размонтировании
};
}, [referenceElement, reference]);
return (
<div ref={floating} style={{ position: 'absolute', left: x ?? 0, top: y ?? 0 }}>
Подсказка
</div>
);
}
useEffect
гарантирует корректную очистку при размонтировании компонента.useFloating освобождают ресурсы,
но при кастомной логике autoUpdate следует использовать явный
cleanup.autoUpdate.computePosition без контроля
жизненного цикла, если элемент динамически создается и
удаляется.Эти практики обеспечивают стабильность интерфейса, предотвращают утечки памяти и гарантируют корректное обновление позиции элементов в любом сценарии использования Floating UI.