React Portal Tutorial
Merry Xmas
Before
Portals是React v16特别有价值的更新, 它的出现打破了父组件-子组件
这种传统模式, 让你在任意DOM节点下操作组件变得简单.
Example
Antd的Modal: 当你打开DeveloperTools你会发现modal所在的节点并非是在#root
下,而是和#root
平级生成的新的Dom. 在v16之前你要做这件事并不容易, 起码你要这样写, 借助rc-util/lib/Portal
来创建, 但是v16来了, 你只需要ReactDOM.createPortal(child, container)
这样.
Journey
代码放到了这里.
这是一个最简单Modal, 点击button,会在button下展现一个Modal, 点击Modal上的Hide
button会隐藏掉Modal.
点击后效果
可以明显的看到在和root
平级地方多出了一个<div>
, 这就是刚刚点击button通过portal创建的modal.
Core Code
// Modal.js
import React from 'react';
import ReactDOM from 'react-dom';
class Modal extends React.Component {
constructor(props) {
super(props);
this.el = document.createElement('div');
}
componentDidMount() {
document.body.appendChild(this.el);
}
componentWillUnmount() {
document.body.removeChild(this.el);
}
render() {
return ReactDOM.createPortal(this.props.children, this.el);
}
}
export default Modal;
// App.js
class App extends Component {
constructor(props) {
super(props);
this.state = {
style: {},
modalVisible: false,
};
this.showModal = this.showModal.bind(this);
}
showModal() {
const dimensions = this.el.getBoundingClientRect();
const left = dimensions.left;
const top = dimensions.top + dimensions.height + 10;
const height = 200;
const width = 200;
const style = { left, top, height, width };
this.setState({
style,
modalVisible: true,
});
}
render() {
const modal = this.state.modalVisible && (
<Modal>
<div className="modal" style={this.state.style}>
<p>Hello, World!</p>
<button onClick={() => this.setState({ modalVisible: false })}>
Hide
</button>
</div>
</Modal>
);
return (
<div className="App">
{ modal }
<button
ref={el => this.el = el}
onClick={this.showModal}>
Show
</button>
</div>
);
}
}
.modal {
background-color: rgba(0,0,0,0.5);
position: fixed;
height: 100%;
width: 100%;
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
- Modal是一个正常的rederable react component.
- 通过
ReactDOM.createPortal(this.props.children, this.el);
在document.body
下创建一个<div>
,并将其子组件加进去. - 生命周期结束(
componentWillUnmount
)将<div>
移除. - 在
App.js
中, 借助render方法,将Modal加进去, 可以放在<div className="App">
下面任意位置,无影响(要保证不出错). - 通过ref获得button的宽高及上下左右位置,设置modal的位置.
After
https://hackernoon.com/using-a-react-16-portal-to-do-something-cool-2a2d627b0202
这一篇文章, 大佬不光将modal建立在root
之外, 更是建立在整个窗体之外.
可以在codepen中体验下.