React 16.0中的新特性——portal及其注意點
簡要介紹:React16.0中釋出了很多新特性,我們來看portal,React提供了一個頂級API—portal,用來將子節點渲染到父節點之外的dom節點
1.基本用法
(1)在React15.X版本中,我們只能講子節點在父節點中渲染,基本用法如下:
render() {
// React需要建立一個新的div來包含子節點
return (
<div>
{this.props.children}
</div>
);
}
但是如果需要將子節點插入到父節點之外的dom呢,React15.x及之前都沒有提供這個功能的API。
(2)React16.0中的portal
render() {
// React不需要建立一個新的div去包含子元素,直接將子元素渲染到另一個
//dom節點中
//這個dom節點可以是任何有效的dom節點,無論其所處於dom樹中的哪個位置
return ReactDOM.createPortal(
this.props.children,
domNode,
);
}
- Portal中的dom節點必須是有效的節點
ReactDOM.createPortal函式的第二個引數,是被插入的dom節點,並且這個dom節點是有效的節點,而不能是通過ref獲得並傳遞的virtual dom節點,舉例來說:
class MyTestableClass extends React.Component{
constructor(){
super();
this.state={
sprop:''
}
}
render(){
return <div>
<Left ref={(input)=>{this.left=input}}>我是左邊的資訊</Left>
<Right sprop={this.state.sprop}>我是右邊的資訊</Right>
</div >
}
componentDidMount(){
var left=this.left;
var state=this.state;
this.setState({
sprop:left
})
}
}
ReactDom.render(
<MyTestableClass/>,document.getElementById('app')
)
在整個元件中,我們將ref獲得的子節點Left,通過props傳遞給了子節點Right,然後在Right子節點中,通過:
class Right extends React.Component{
constructor(){
super();
}
render(){
var props=this.props;
if(this.props.sprop){
return <div className="right">{this.props.children}</div>
}else{
return ReactDOM.createPortal(
this.props.children,
this.props.sprop
);
}
}
componentWillReceiveProps(nextProp){
console.log(nextProp.sprop);
}
}
我們嘗試將子節點插入到通過props傳遞過來的另一個節點this.props.sprop中,因為this.props.sprop並不是一個valid dom node,它是一個virtual dom node,因此會報錯:
//Target container is not a DOM element.
3.通過Portals實現事件冒泡
雖然一個portal可以插入到任何一個存在dom樹中,但是通過Portal節點插入到其他dom中的節點,跟其他的React普通的子節點表現相同。在父節點下,通過Portal插入的子節點也可以共享context。
對於事件冒泡,從Portal節點中觸發的事件,雖然可能改變了節點所處的位置,但是在HTML結構中的父包含節點是可以拿到這個事件的。
<html>
<body>
<div id="app-root"></div>
<div id="modal-root"></div>
</body>
</html>
在上面的HTML結構中,通過Portal, 在Button真實位置是包含在“modal-root”下,但是因為在HTML的結構中Button所在的父節點的HTML結構是包含“app-root”中的,因此在“modal-root”中出發的事件,也可以冒泡到“app-root”中。