需求分析
很多時候我們需要通過彈窗/對話框來完成交互,因此這個公共的彈窗組件需要實現以下功能:
- 組件動態地傳入彈窗的標題和內容
- 點擊確定和取消按鈕之后,執行組件傳入的回調函數
樣式布局
JSX代碼如下:(通過 styled-components 開發樣式)
const JSXdom = (
<div>
<Mask />
<ModalWrapper>
<ModalContent>
<p className="title">{title}</p>
<div className="content">{tips}</div>
<BtnGroup >
<div
className="btn left"
onClick={() => this.onCancel(handleCancel)}
>取消</div>
<div
className="btn right"
onClick={() => this.onOk(handleOk)}
>確定</div>
</BtnGroup>
</ModalContent>
</ModalWrapper>
</div>
)
首先我們需要一個遮住層Mask作為背景,實現頁面背景變暗的效果
export const Mask = styled.div`
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
height: 100%;
background-color: rgba(0, 0, 0, 0.65);
z-index: 1000;
`
然后用過絕對定位 + transform 實現彈窗的水平垂直居中
并對標題、內容、確認和取消按鈕進行簡單的布局
export const ModalWrapper = styled.div`
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1000;
`
export const ModalContent = styled.div`
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 20px 20px 0 20px;
width: 60%;
background-color: #fff;
border-radius: 4px;
text-align: center;
.title {
font-size: 18px;
margin-bottom: 15px;
}
.content {
width: 100%;
font-size: 14px;
line-height: 20px;
margin-bottom: 15px;
word-wrap: break-word;
}
`
export const BtnGroup = styled.div`
display: flex;
align-items: center;
text-align: center;
font-size: 14px;
.btn {
flex: 1;
line-height: 22px;
padding: 10px 0;
border-top: 1px solid whitesmoke;
background-color: #fff;
&.left {
border-right: 1px solid whitesmoke;
}
}
`
核心函數
核心函數的職責如下:
- 創建一個空的div節點(createElement)
- 將上述JSX渲染到div節點上(ReactDom.render)
- 將div節點掛載到body上(appendChild)
代碼如下:
success() {
this.dom = document.createElement('div')
const JSXDOM = ()
ReactDom.render(JSXDOM,this.dom)
document.dody.appendChild(this.dom)
}
這樣,我們就成功將彈窗渲染到頁面上了
回調函數
當用戶點擊確定或取消時,我們必須將控制權交還給 調用彈窗的組件,由組件來執行后續的操作
因此,success函數需要接收 handleOk 和 handleCancel 這兩個函數作為參數
為了使得彈窗消失,我們還需要再封裝 onOk 和 onCancel 函數,讓他們去調用組件的回調函數,并且清除this.dom
首先將清除dom的任務封裝成一個輔助函數 removeDom
removeDom() {
this.dom && this.dom.remove()
/*
寫法等同于
if(this.dom){
this.dom.remove()
}
*/
}
然后編寫onOk和onCancel函數
onOk(handleOk) {
(handleOk instanceof Function) && handleOk()
this.removeDom()
}
onCancel(handleCancel) {
(handleCancel instanceof Function) && handleCancel()
this.removeDom()
}
完整的代碼如下:
import React from 'react'
import ReactDOM from 'react-dom'
import {
Mask,
ModalWrapper,
ModalContent,
BtnGroup
} from './style'
export default {
dom: null,
success ({title,tips,handleOk,handleCancel}) {
this.removeDom()
this.dom = document.createElement('div')
const JSXdom = (
<div>
<Mask />
<ModalWrapper>
<ModalContent>
<p className="title">{title}</p>
<div className="content">{tips}</div>
<BtnGroup >
<div
className="btn left"
onClick={() => this.onCancel(handleCancel)}
>取消</div>
<div
className="btn right"
onClick={() => this.onOk(handleOk)}
>確定</div>
</BtnGroup>
</ModalContent>
</ModalWrapper>
</div>
)
ReactDOM.render(JSXdom,this.dom)
document.body.appendChild(this.dom)
},
onCancel (handleCancel) {
(handleCancel instanceof Function) && handleCancel();
this.removeDom()
},
onOk (handleOk) {
(handleOk instanceof Function) && handleOk();
this.removeDom()
},
removeDom() {
this.dom && this.dom.remove()
}
}
組件調用
import Modal from './xxx.js'
class W extends from Component {
render(){
return(
<div onClick={this.handleClick}>點擊</div>
)
}
handleClick() {
Modal.success({
title: '標題',
tips: '測試彈窗',
handleOk: () => {
console.log('Ok')
},
handleCancel: () => {
console.log('Cancel')
}
})
}
}
注意:組件傳入的 hanldleOk 和 handleCancel 最好使用箭頭函數的方式來定義
這樣才能確保函數在執行的時候,this的指向為當前組件