Portal
Header
Чтобы продемонстрировать для чего нужен портал, реализуем стандартную задачу. Добавим в наше приложение header, который
всегда будет в зафиксированном положение. Компонент Header.tsx отрисуем в App.tsx. В Header.tsx отрисуем
компонент Cart.tsx
export const Header = () => {
return (
<div className={s.headerWrapper}>
<div className={s.container}>
<h3>logotype</h3>
<Cart />
</div>
</div>
)
}.headerWrapper {
background-color: lightgray;
margin-bottom: 20px;
position: fixed;
height: 60px;
left: 0;
right: 0;
top: 0;
width: 100%;
display: flex;
align-items: center;
}
.container {
display: flex;
width: 1440px;
justify-content: space-between;
margin: 0 auto;
align-items: center;
}И модалка сломалась 😢. Почему так ?

Потому что сейчас наша модалка рендерится в том месте где мы указали.
Элемент с position: absolute позиционируется относительно ближайшего предка с заданным
позиционированием, то есть с position: relative, position: absolute, position: fixed или
position: sticky. Если такой предок не найден, элемент позиционируется относительно всего
документа (то есть относительно body или html).


Portal
React-порталы позволяют рендерить компонент за пределами его родительского DOM-дерева. Это особенно полезно для
элементов, которые должны отображаться поверх остальной части интерфейса, например, для модальных окон,
всплывающих сообщений и других overlay-компонентов. Порталы помогают избежать проблем с CSS-стилями, z-index, и
контекстом, которые могут возникать, если модальное окно находится внутри DOM-дерева родительского компонента.
Как работают порталы
Порталы создаются с помощью функции ReactDOM.createPortal, которая принимает два аргумента:
React-элемент— то, что нужно отобразить.DOM-узел— контейнер, куда будет рендериться элемент, например,document.bodyили любой другой элемент, находящийся вне основного приложения.
Преимущества порталов
-
Упрощенное управление z-index: Поскольку модалка рендерится в
#modal-rootвне основного дерева, легче управлять её позиционированием и наложением. -
Избежание конфликтов CSS: Стили модального окна не будут взаимодействовать с родительскими элементами.
-
Улучшение пользовательского опыта: Легче управлять событиями, например, кликами за пределами модального окна для его закрытия.
export const Modal = ({ onClose, open, children, modalTitle }: Props) => {
return (
<>
{open && (
<>
{createPortal(
<div className={s.overlay}>
<div className={s.content}>
<h3 className={s.title}>{modalTitle}</h3>
<hr />
{children}
<button className={s.closeIcon} onClick={onClose}>
x
</button>
</div>
</div>,
document.body
)}
</>
)}
</>
)
}Результат. Модалка снова отрабатывает верно, т.к. рендерится не внутри header, а в body. И теперь мы уверены,
что у модалки никогда не сломаются стили 🚀
