Portal

Portal

Portals 提供了一个最好的在父组件包含的DOM结构层级外的DOM节点渲染组件的方法。

应用场景:当在react中想实现一个类似于dialog的全局组件,此时该组件的样式很容易收到父组件、兄弟组件的干扰(z-index,拼爹原则:当兄弟组件都有z-index,一个大于另一个的时候,小的那个组件的子组件的z-index就算在大也没有用,优先级仍然还是低于z-index较大的那个组件)

1
2
// 第一个参数child是可渲染的react子项,比如元素,字符串或者片段等。第二个参数container是一个DOM元素。
ReactDOM.createPortal(child,container);

普通的组件,子组件的元素将挂载到父组件的DOM节点中。

一个典型的用法就是当父组件的dom元素有 overflow:hidden 或者 z-inde 样式,而你又需要显示的子元素超出父元素的盒子。举例来说,如对话框,悬浮框,和小提示。

示例:

App.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import React, {Component} from 'react';
import './App.css'
import PortalDialog from "./components/PortalDialog";
class App extends Component {
state = {
isShow:false
}
render() {
return (
<div className="box">
<div className="left"></div>
<div className="right">
<button onClick={() => {
this.setState({
isShow:true
})
}}>click</button>
{
this.state.isShow && <PortalDialog onClose={() => {
this.setState({
isShow:false
})
}
}></PortalDialog>
}
</div>
</div>
);
}
}

export default App;

App.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
*{
margin: 0;
padding: 0;
}
.left,.right {
height: 100vh;
}
.box {
display: flex;
}
/*此时left的z-index明显要大于right的z-index,因此,right结构下的组件无论它的z-index有多大,都不会超过left的优先级,因为拼爹失败。*/
.left {
width: 200px;
background: yellow;
position: relative;
z-index: 10;
}
.right{
flex: 1;
background: blue;
position: relative;
z-index: 5;
}

PortalDialog.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import React, {Component} from 'react';
import {createPortal} from "react-dom";

class PortalDialog extends Component {
render() {
return createPortal (
<div style={{width:'100%',height:'100%',position:'fixed',left:0,top:0,background:'rgba(0,0,0,0.7)',zIndex:9999}}>
Dialog-
<div>loading-正在加载中。。</div>
<button onClick={this.props.onClose}>close</button>
</div>
,document.body);
}
}

export default PortalDialog;

portal中的事件冒泡

虽然通过portal渲染的元素在父组件的盒子之外,但是渲染的dom节点仍在React的元素树上,在那个dom元素上的点击事件仍然能在dom树中监听到。