什么东西
Portals 提供了一种很好的将子节点渲染到父组件以外的 DOM 节点的方式。
Plain Text
ReactDOM.createPortal(child, container)
第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或碎片。第二个参数(container)则是一个 DOM 元素。
简单来说就是提供一种可以自定义安排组件位置的API。一般情况下组件都是被安插在距离最近的父节点上,通过这个API,可以将组件安插在指定的DOM节点上。
Plain Text
render() {
// React does not create a new div. It renders the children into domNode
.
// domNode
is any valid DOM node, regardless of its location in the DOM.
return ReactDOM.createPortal(
this.props.children,
domNode,
);
}
典型用例是当父组件有 overflow: hidden 或 z-index 样式,但你需要子组件能够在视觉上“跳出(break out)”其容器。例如,对话框、hovercards以及提示框:
事件冒泡
一个从 portal 内部会触发的事件会一直冒泡至包含 React 树 的祖先。
Plain Text
<html>
<body>
<div id="app-root"></div>
<div id="modal-root"></div>
</body>
</html>
Plain Text
// These two containers are siblings in the 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() {
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() {
// This will fire when the button in Child is clicked,
// updating Parent's state, even though button
// is not direct descendant in the DOM.
this.setState(prevState => ({
clicks: prevState.clicks + 1
}));
}
render() {
return (
<div onClick={this.handleClick}>
<p>Number of clicks: {this.state.clicks}</p>
<p>
Open up the browser DevTools
to observe that the button
is not a child of the div
with the onClick handler.
</p>
<Modal>
<Child />
</Modal>
</div>
);
}
}
function Child() {
// The click event on this button will bubble up to parent,
// because there is no 'onClick' attribute defined
return (
<div className="modal">
<button>Click</button>
</div>
);
}
ReactDOM.render(<Parent />, appRoot);
在上面的例子中,app-root和modal-root是两个平级的dom节点,从结构上来说,在modal-root上触发的事件不会被app-root捕获。
但是我们使用createPortal接口将Modal组件的children绑定到modal-root节点上,然后再Parent组件中,包裹Modal组件并监听onClick事件,并将Parent组件绑定到app-root节点上。
从真实的DOM结构上来看,mdal-root节点上的Modal组件中Child组件的onClick事件不应该被在app-root中Parent组件捕获,但从虚拟DOM的解构上来看,Modal却是Parent组件的子节点,这个事件冒泡还是比较遵循虚拟DOM的。。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。