Portals
Portals提供了一种将子节点渲染都存在于父组件以外的DOM节点的优秀方案。
ReactDOM.createPortal(child,container)第一个参数(child)是任何可渲染的React子元素,例如一个元素,字符串或fragment。第二个参数(container)是一个DOM元素。
用法
通常来讲,当你从组件的render方法返回元素的时,该元素将被挂在到DOM节点中里其最近的父节点。
render(){
return (
<div>
{this.props.children}
</div>
)
}然而,有时候将子元素插入到DOM节点中的不同位置也是有好处的:
render(){
return ReactDOM.createPortal(
this.props.children,
domNode
)
}一个 portal 的典型用例是当父组件有overflow:hidden或z-index样式的时候,但你需要子组件能够在视觉上"跳出"容器。例如,对话框、悬浮框以及提示框。
通过Portal进行事件冒泡
尽管 portal 可以被防止在 DOM 树中的任何地方,但在任何其他方面,其行为和普通的 React 子节点行为一致。由于 portal 仍存在于 React 树,且与 DOM 树中的位置无关,那么无论其子节点是否是 portal,像 context 这样的功能特性是不变的。 这包括事件冒泡。一个从 portal 内部触发的事件会一直冒泡至 React 树的组件。即使这些元素并不是 DOM 树中的祖先。
<html>
<body>
<div id="app-root"></div>
<div id="modal-root"></div>
</body>
</html>在#app-root里面Parent组件能捕获到未被捕获的从兄弟节点#modal-root冒泡上来的事件。
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 () {
// 在 Modal 的所有子元素被挂载后,
// 这个 portal 元素会被嵌入到 DOM 树中,
// 这意味着子元素将被挂载到一个分离的 DOM 节点中。
// 如果要求子组件在挂载时可以立刻接入 DOM 树,
// 例如衡量一个 DOM 节点,
// 或者在后代节点中使用 'autoFocus',
// 则需添加 state 到 Modal 中,
// 仅当 Modal 被插入 DOM 树中才能渲染子元素。
modalRoot.appendChild(this.el);
}
componentDidUnMount () {
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 () {
//当子元素中的按钮被点击时
// 这个将会触发更新父元素阿state
// 即视这个按钮在dom中不是直接的关联的后台
this.setState(state => ({
clicks: state.clicks + 1
}))
}
render(){
return (
<div onClick={this.handleClick}>
<p>Number of clicks : {this.state.clicks}</p>
<Modal>
<Child />
</Modal>
</div>
)
}
}
function Child(){
return (
<div className = "modal">
<button>clcik</button>
</div>
)
}
ReactDOM.render(<Parent /> , appRoot)在父组件里捕获一个来自portal冒泡上来得事件,使之能够在开发时具有不完全依赖portal得更灵活得抽象。例如,如果你在渲染一个<Modal />组件,无论其是否采用portal实现,父组件都能够捕获其事件。
