Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
React Порталы (Portals)
Портал — это специальный механизм React, который позволяет отрендерить компонент в другом месте DOM дерева, вне его обычной иерархии. Это полезно для модальных окон, выпадающих меню и тултипов.
Что такое портал
Обычно React компонент рендерится как дочерний элемент своего родителя в DOM. Портал позволяет рендерить компонент в произвольный узел DOM, которому не является родителем компонента.
Базовое использование
import { createPortal } from react;
const Modal = ({ isOpen, children }) => {
if (!isOpen) return null;
// Рендерим содержимое в элемент с id="modal-root"
return createPortal(
<div className="modal-overlay">
<div className="modal-content">
{children}
</div>
</div>,
document.getElementById(modal-root)
);
};
В HTML необходимо добавить целевой контейнер:
<html>
<body>
<div id="root"></div>
<div id="modal-root"></div> <!-- Портал рендерится сюда -->
</body>
</html>
Практический пример: модальное окно
interface ModalProps {
isOpen: boolean;
onClose: () => void;
title: string;
children: React.ReactNode;
}
const Modal = ({ isOpen, onClose, title, children }: ModalProps) => {
if (!isOpen) return null;
return createPortal(
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
<div className="bg-white rounded-lg shadow-lg p-6 w-96">
<div className="flex justify-between items-center mb-4">
<h2 className="text-xl font-bold">{title}</h2>
<button
onClick={onClose}
className="text-gray-400 hover:text-gray-600"
>
✕
</button>
</div>
<div>{children}</div>
<button
onClick={onClose}
className="mt-4 px-4 py-2 bg-blue-500 text-white rounded"
>
Закрыть
</button>
</div>
</div>,
document.body
);
};
// Использование
const App = () => {
const [isOpen, setIsOpen] = useState(false);
return (
<div>
<button onClick={() => setIsOpen(true)}>Открыть модаль</button>
<Modal isOpen={isOpen} onClose={() => setIsOpen(false)} title="Привет">
<p>Содержимое модали</p>
</Modal>
</div>
);
};
Портал для выпадающего меню
const Dropdown = ({ trigger, items }: Props) => {
const [isOpen, setIsOpen] = useState(false);
const ref = useRef<HTMLDivElement>(null);
// Получи позицию триггера
const rect = ref.current?.getBoundingClientRect();
return (
<div ref={ref} className="relative inline-block">
<button onClick={() => setIsOpen(!isOpen)}>{trigger}</button>
{isOpen && createPortal(
<ul
className="absolute bg-white border rounded shadow-lg z-10"
style={{
top: (rect?.bottom || 0) + "px",
left: (rect?.left || 0) + "px",
width: (rect?.width || 0) + "px"
}}
>
{items.map(item => (
<li key={item.id} className="px-4 py-2 hover:bg-gray-100">
{item.label}
</li>
))}
</ul>,
document.body
)}
</div>
);
};
Когда использовать порталы
1. Модальные окна
- Всегда рендерятся в корне, не под другими элементами
- Проще управлять z-index
2. Тултипы и Popover
- Нужно выйти за границы контейнера
- Позиционирование относительно документа
3. Выпадающие меню
- Избежать
overflow: hiddenна родителях - Всегда видно сверху остальных элементов
4. Уведомления (Toast/Notifications)
- Центральный список уведомлений
- Легко управлять слоем UI
Важные моменты
Event Bubbling через портал
События в портале всё ещё всплывают в дерево компонентов (не в DOM):
const Parent = () => {
const handleClick = (e: React.MouseEvent) => {
console.log("Parent clicked");
};
return (
<div onClick={handleClick}>
{createPortal(
<button onClick={(e) => console.log("Button clicked")}>
Клик
</button>,
document.body
)}
</div>
);
};
// При клике на кнопку выведет:
// Button clicked
// Parent clicked (всплывает в дерево React!)
Захват фокуса (Aria Dialog)
Для доступности в модали нужно управлять фокусом:
const AccessibleModal = ({ isOpen, onClose }: Props) => {
const modalRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (isOpen) {
modalRef.current?.focus();
}
}, [isOpen]);
return createPortal(
<div
ref={modalRef}
role="dialog"
aria-modal="true"
tabIndex={-1}
>
{/* содержимое */}
</div>,
document.body
);
};
Альтернативы
- CSS
position: fixed— проще для простых случаев - CSS
position: absoluteсposition: relativeродителем - Shadow DOM — для полной изоляции стилей (редко)
Резюме
Порталы — мощный инструмент для управления слоями UI. Используй их для модалей, тултипов и выпадающих меню, когда стандартное DOM-дерево не подходит.