3.11 Порталы
Доступны с 16 версии.
Порталы предоставляют первоклассный способ отображения дочерних элементов в узел DOM, который существует вне иерархии DOM родительского компонента.
ReactDOM.createPortal(child, container)
Первым аргументом (child) является любой отображаемый потомок React,
такой как элемент, строка или фрагмент. Второй аргумент (container)
является элементом DOM.
Как правило, когда вы возвращаете элемент из метода отрисовки компонента, он монтируется в DOM как дочерний элемент ближайшего родительского узла:
render() {
// React монтирует новый div и отрисовывает в него потомок
return (
<div>
{this.props.children}
</div>
);
}
Однако иногда полезно вставлять дочерний элемент в другое место в DOM:
render() {
// React не создаёт новый div. Он отрисовывает потомок в `domNode`.
// `domNode` - это всегда валидный DOM-узел, независимо от его места в DOM.
return ReactDOM.createPortal(
this.props.children,
domNode,
);
}
Типичный вариант использования порталов - это когда родительский компонент
имеет overflow: hidden или z-index стиль, но вам нужно, чтобы дочерний компонент
визуально «выходил» из своего контейнера. Например, диалоги, всплывающие подсказки.
Замечание!
Важно помнить, что при работе с порталами вам необходимо следить за тем, что вы следуете рекомендациям по общедоступности.Несмотря на то, что портал может быть где угодно в дереве DOM, он ведет себя как обычный дочерний элемент React во всех отношениях. Такие функции, как контекст, работают как и ранее, независимо от того, является ли дочерний элемент порталом, поскольку портал все еще существует в дереве React независимо от его положения в дереве DOM.
Это же касается и всплытия события. Событие, созданное внутри портала, будет распространяться к предкам в объемлющем дереве React, даже если они не являются предками в дереве DOM. Представим следующую структуру HTML:
<html>
<body>
<div id="app-root"></div>
<div id="modal-root"></div>
</body>
</html>
Компонент Parent в #app-root мог бы поймать неперехваченное всплывающее
событие из соседнего узла #modal-root.
// Эти два контейнера являются соседями в DOM
const appRoot = document.getElementById('app-root');
const modalRoot = document.getElementById('modal-root');
class Modal extends React.Component {
constructor(props) {
super(props);
this.el = document.createElement('div');
}
componentDidMount() {
/*
Элемент портала вставлен в дерево DOM после того, как потомки Modal
были монтированы, что означает, что потомки будут монтированы в отдельный
узел DOM.
Если дочерний компонент требует присоединения к дереву DOM сразу после
его монтирования, например, для измерения узла DOM или использования
«autoFocus» в потомке, добавьте состояние в Modal и отрисуйте дочерние
элементы, после того, как Modal будет вставлен в DOM дерево.
*/
modalRoot.appendChild(this.el);
}
componentWillUnmount() {
modalRoot.removeChild(this.el);
}
render() {
return ReactDOM.createPortal(
this.props.children,
this.el,
);
}
}
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {clicks: 0};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// Он сработает, когда кнопка в Child будет нажата,
// обновляя состояние Parent, даже если кнопка
// не является его прямым потомком в DOM.
this.setState(prevState => ({
clicks: prevState.clicks + 1
}));
}
render() {
return (
<div onClick={this.handleClick}>
<p>Число кликов: {this.state.clicks}</p>
<p>
Откройте DevTools браузера,
чтобы увидеть, что кнопка button
не является потомком div
с обработчиком onClick.
</p>
<Modal>
<Child />
</Modal>
</div>
);
}
}
function Child() {
// Событие клика на этой кнопке будет всплывать к Parent,
// так как нет заданного 'onClick' атрибута
return (
<div className="modal">
<button>Click</button>
</div>
);
}
ReactDOM.render(<Parent />, appRoot);
Захват события, всплывающего из портала в родительском
компоненте, позволяет создавать более гибкие абстракции, которые по своей сути не зависят от
порталов. Например, если вы отрисовываете компонент <Modal />, родитель может
захватывать свои события независимо от того, реализован ли он с помощью порталов.
