Popper.js — это мощная библиотека для управления позиционированием всплывающих элементов относительно целевых элементов на странице. В контексте React её использование требует правильного управления состоянием компонентов и ссылками на DOM-элементы. Основной подход строится вокруг Ref API и хуков React.
Popper.js создаёт объект поппера через функцию
createPopper(reference, popper, options), где:
В React прямой доступ к DOM осуществляется через
useRef:
import { useRef, useEffect, useState } from 'react';
import { createPopper } from '@popperjs/core';
function Tooltip({ text }) {
const referenceRef = useRef(null);
const popperRef = useRef(null);
const [popperInstance, setPopperInstance] = useState(null);
useEffect(() => {
if (referenceRef.current && popperRef.current) {
const instance = createPopper(referenceRef.current, popperRef.current, {
placement: 'top',
modifiers: [{ name: 'offset', options: { offset: [0, 8] } }],
});
setPopperInstance(instance);
return () => instance.destroy();
}
}, []);
return (
<>
<button ref={referenceRef}>Наведи на меня</button>
<div ref={popperRef} style={{ visibility: 'hidden' }}>{text}</div>
</>
);
}
Ключевые моменты:
Для динамических тултипов или всплывающих меню часто используют
useState для отслеживания видимости:
const [visible, setVisible] = useState(false);
<button
ref={referenceRef}
onMouseEn ter={() => setVisible(true)}
onMouseLe ave={() => setVisible(false)}
>
Наведи на меня
</button>
<div
ref={popperRef}
style={{ visibility: visible ? 'visible' : 'hidden' }}
>
{text}
</div>
Такой подход позволяет интегрировать Popper.js с любыми интерактивными элементами, сохраняя реактивность состояния React.
Popper.js поддерживает модификаторы, которые позволяют гибко настраивать поведение поппера:
Пример с стрелкой:
<div ref={popperRef} style={{ visibility: visible ? 'visible' : 'hidden' }}>
<div ref={arrowRef} data-popper-arrow></div>
{text}
</div>
createPopper(referenceRef.current, popperRef.current, {
placement: 'top',
modifiers: [
{ name: 'offset', options: { offset: [0, 10] } },
{ name: 'arrow', options: { element: arrowRef.current } },
{ name: 'flip', options: { fallbackPlacements: ['bottom', 'right', 'left'] } }
],
});
Использование data-popper-arrow автоматически учитывает
размеры стрелки при позиционировании.
Для удобства можно создать кастомный хук, который инкапсулирует логику Popper.js:
import { useRef, useEffect, useState } from 'react';
import { createPopper } from '@popperjs/core';
export function usePopper(options) {
const referenceRef = useRef(null);
const popperRef = useRef(null);
const [instance, setInstance] = useState(null);
useEffect(() => {
if (referenceRef.current && popperRef.current) {
const popperInstance = createPopper(referenceRef.current, popperRef.current, options);
setInstance(popperInstance);
return () => popperInstance.destroy();
}
}, [options]);
return { referenceRef, popperRef, instance };
}
Использование хука в компоненте:
function Tooltip({ text }) {
const { referenceRef, popperRef } = usePopper({
placement: 'bottom',
modifiers: [{ name: 'offset', options: { offset: [0, 12] } }],
});
const [visible, setVisible] = useState(false);
return (
<>
<button
ref={referenceRef}
onMouseEn ter={() => setVisible(true)}
onMouseLe ave={() => setVisible(false)}
>
Наведи на меня
</button>
<div ref={popperRef} style={{ visibility: visible ? 'visible' : 'hidden' }}>
{text}
</div>
</>
);
}
Такой подход минимизирует повторение кода и облегчает поддержку больших проектов.
Popper.js автоматически отслеживает размеры reference и popper, но в React часто требуется принудительно обновлять позицию при изменении состояния:
useEffect(() => {
if (instance) {
instance.update();
}
}, [visible, text]);
Это особенно важно, если контент поппера динамический или изменяется размер кнопки/ссылки.
Для всплывающих элементов, которые должны отображаться поверх всего интерфейса, удобно использовать React Portal:
import { createPortal } from 'react-dom';
{visible && createPortal(
<div ref={popperRef}>{text}</div>,
document.body
)}
Popper.js корректно позиционирует элементы вне DOM-иерархии
родительского компонента, если передать ссылку на DOM в
createPopper.
Эти подходы обеспечивают надёжную и производительную интеграцию Popper.js в проекты на React, сохраняя декларативный стиль и реактивность компонентов.