Реактивность и refs

Floating UI — это библиотека для позиционирования всплывающих элементов относительно других элементов в веб-приложениях. Одной из ключевых особенностей является возможность интеграции с реактивными фреймворками (React, Vue, Solid и др.), что позволяет динамически обновлять позицию элементов при изменении состояния приложения. Центральным механизмом здесь являются refs — ссылки на DOM-узлы, используемые для вычисления позиции.

Использование refs

Refs в Floating UI служат для привязки элемент-всплывающее окно и элемент-опорный объект. Библиотека требует два обязательных ref:

  1. Reference Element — элемент, относительно которого позиционируется всплывающее окно.
  2. Floating Element — само всплывающее окно.

Пример с React:

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

function Tooltip() {
  const { x, y, reference, floating, strategy } = useFloating();

  return (
    <>
      <button ref={reference}>Наведи на меня</button>
      <div ref={floating} style={{ position: strategy, top: y ?? 0, left: x ?? 0 }}>
        Всплывающая подсказка
      </div>
    </>
  );
}

В этом примере reference и floating — это функции, которые автоматически присваивают DOM-элементы внутренним переменным Floating UI. Обратите внимание, что x и y могут быть undefined до момента первого расчета, поэтому важно использовать безопасные fallback-значения.

Реактивность позиций

Floating UI предоставляет реактивное API, которое позволяет отслеживать изменения размера или положения элементов и пересчитывать позицию. Основные подходы:

  • autoUpdate — функция, которая автоматически обновляет позицию при изменении размера окна, прокрутке или мутации DOM.
import { autoUpdate } from '@floating-ui/dom';

const cleanup = autoUpdate(referenceElement, floatingElement, updatePosition);

function updatePosition() {
  computePosition(referenceElement, floatingElement, { placement: 'bottom' })
    .then(({ x, y }) => {
      Object.assign(floatingElement.style, {
        left: `${x}px`,
        top: `${y}px`,
      });
    });
}
  • Реактивные стейт-значения — использование x и y в React/Vue позволяет автоматически пересчитывать стили при обновлении позиции.

Интеграция с Vue и Solid

Для реактивных фреймворков refs интегрируются через ref или createSignal. В Vue это выглядит так:

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

export default {
  setup() {
    const reference = ref(null);
    const floating = ref(null);

    const { x, y, strategy } = useFloating(reference, floating);

    return { reference, floating, x, y, strategy };
  },
};

В Solid подход аналогичен, но используется реактивная система сигналов:

import { createSignal } from 'solid-js';
import { useFloating } from '@floating-ui/solid';

const [reference, setReference] = createSignal(null);
const [floating, setFloating] = createSignal(null);

const { x, y, strategy } = useFloating(reference, floating);

Пограничные случаи и рекомендации

  • Отсутствие ref: Если reference или floating не инициализированы, вычисления не выполняются. Нужно проверять, что DOM-элементы существуют.
  • Изменение размеров: Для всплывающих элементов с динамическим контентом рекомендуется использовать autoUpdate для поддержания точной позиции.
  • Комбинация нескольких модификаторов: Floating UI позволяет подключать middleware (offset, flip, shift). Реактивность ref обеспечивает корректное применение этих модификаторов при любом изменении состояния.

Работа с динамическими ref

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

const reference = null;
const floating = null;

useEffect(() => {
  if (reference && floating) {
    computePosition(reference, floating, { placement: 'top' }).then(updateStyles);
  }
}, [reference, floating]);

Такой подход гарантирует корректную позицию даже для элементов, которые рендерятся после загрузки страницы.

Вывод

Refs и реактивность в Floating UI образуют ядро динамического позиционирования. Правильная настройка reference и floating, совместно с автообновлением позиции через autoUpdate или реактивные состояния, обеспечивает стабильную работу всплывающих элементов независимо от изменений DOM, размеров окна и внутренних стейтов приложения. Этот подход универсален и одинаково эффективен как в React, так и в Vue или Solid, обеспечивая гибкое управление позиционированием в сложных интерфейсах.