本文以Antd中的Modal组件作为案例剖析,重点讲述如何用Portals手动实现Modal组件。
Antd中Modal组件效果展示
(1)简易版Modal使用--代码演示
import React, { Component } from 'react'
import {Modal,Button} from 'antd'
class DialogPage extends Component {
state={
isShow:false,
}
toggle=()=>{
this.setState({
isShow:!this.state.isShow
})
}
handleOk=()=>{
this.setState({
isShow:false
})
}
handleCancel=()=>{
this.setState({
isShow:false
})
}
render () {
return (
<div>
<Button type={'primary'} onClick={this.toggle}>toggle</Button>
<Modal
title="Basic Modal"
visible={this.state.isShow}
onOk={this.handleOk}
onCancel={this.handleCancel}
>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
</Modal>
</div>
)
}
}
export default DialogPage
(2)效果预览
请注意:
代码中Modal组件与Button平齐。当dom渲染完成后,Modal组件与根结点root平齐。概述:
弹窗类组件的要求弹窗内容在A处声明,却在B处展示。react中相当于弹窗内容看起来被render到一个组件里面去,实际改变的是网页上另一处的DOM结构,这个显然不符合正常逻辑。但是通过使用框架提供的特定API创建组件实例并指定挂载目标仍可完成任务。
手动实现简易版Modal组件
Portals提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案,这能够帮助我们解答上面的疑问。
(1)自定义Modal组件
// Modal组件
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import './modal.css'
import {createPortal} from 'react-dom'
class Modal extends Component {
constructor (props){
super(props)
this.state={
node:undefined
}
}
static getDerivedStateFromProps(props, state){
const document=window.document
if(props.visible){ // visible 为true时,body中新增div,为createPortal提供一个挂载节点。
const node=document.createElement('div')
document.body.appendChild(node)
return {
node // 将挂载的Dom节点存储起来,方便移除时使用
};
}
if(state.node){ // visible为false时,移除对应的dom
document.body.removeChild(state.node)
}
return null
}
render () {
const {visible,title,onOk,onCancel}=this.props
if(!visible){
return null;
}
return createPortal( //第一个参数(child)是任何可渲染的 React 子元素,例如一个元素,字符串或 fragment。第二个参数(container)是一个 DOM 元素。
<div className='modalWrapper'>
{
title&&(
<div className='modalTitle'>
{title}
</div>
)
}
{this.props.children}
<div className='modalFooter'>
<button onClick={onCancel}>取消</button>
<button onClick={onOk}>确认</button>
</div>
</div>,
this.state.node
)
}
}
Modal.propTypes={
visible:PropTypes.bool
}
Modal.defaultProps={
visible:true
}
export default Modal
// 自定义Modal使用页面
import React, { Component } from 'react'
import Modal from './Modal'
class ModalPage extends Component {
state={
isShow:false,
isShow2:false
}
toggle=()=>{
this.setState({
isShow:!this.state.isShow
})
}
handleOk=()=>{
this.setState({
isShow:false
})
}
handleCancel=()=>{
this.setState({
isShow:false
})
}
render () {
return (
<div>
<button onClick={this.toggle}>toggle</button>
<Modal
title="custom Modal"
visible={this.state.isShow}
onOk={this.handleOk}
onCancel={this.handleCancel}
>
<p>自定义Modal</p>
<p>自定义Modal</p>
</Modal>
</div>
)
}
}
export default ModalPage
.modalWrapper{
position:absolute;
z-index: 100;
top:50%;
left:50%;
transform: translate(-50%,-50%);
width:200px;
border:1px solid red;
text-align: center;
}
.modalTitle{
border-bottom: 1px solid red;
}
.modalFooter{
border-top: 1px solid red;
}
(2) 效果展示
如图所示,我们实现了Dom渲染穿越了层级。
(3) 概述
总的来说上面的Modal组件实现很容易,主要关注两点:
1.熟悉Portals的用法,可以看官方文档有更详细的解释。
2.关闭Modal时,同时移除对应Dom。代码中有对应的注释。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。