当下前端届最火的技术之一莫过于React + Redux + webpack的技术结合。最近公司内部也正在转react,这周主要作了个React的modal组件,接下来谈下具体实现过程。html
虽然React基于虚拟DOM,但他的JSX语法仍是离不开最基本的HTML。第一步要作的就是经过HTML&&CSS实现Dialog垂直水平居中框。HTML结构以下:前端
<div className="m-mask"></div> <div className="m-dialog"> <div className="md-dialog"> <div className="md-dialog-title"> <h4>{title}</h4> <span className="btn"> <i className="iconfont">×</i> </span> </div> <div className="md-dialog-content"> {this.props.children} </div> <div className="md-dialog-foot"> <a href="#" className="btns">取消</a> <a href="#" className="btns btns-blue">肯定</a> </div> </div> </div>
ps: JSX语法的className对应于HTML中的class,其次文中的iconfont图标被换成了×
。
而后写下对应的CSS样式。此处主要说明一下主要的样式布局原理,细节略过。
Modal框的背景mask样式经过position:fixed + top/right/bottom/left:0 + height: 100%
实现。
不定宽高的主体内容水平垂直居中的实现经过position:fixed + top/left: 50% + translate(-50%, -50%)
实现。react
有了已经想好的布局样式,开始实现最基本的Modal组件。由于须要动态控制组件的显隐,因此组件的显隐在内部要经过state方便控制,而其余属性则经过props实现。modal.js代码以下:webpack
import React, { Component, PropTypes } from 'react' const defaultProps = { show: false, title: '', zIndex: 1000, onOk: () => {}, onCancel: () => {}, } const propTypes = { title: PropTypes.string, zIndex: PropTypes.number, onOk: PropTypes.func, onCancel: PropTypes.func, } export default class Modal extends Component { constructor(props) { super(props) this.state = {show: props.show} } render() { return ( // JSX语法的HTML ); } } Modal.defaultProps = defaultProps Modal.propTypes = propTypes
不过显隐内部经过state控制,但父组件仍是须要经过props传递初始默认值。并且来回调用同一个modal时,父组件是经过props中的show属性控制。内部的state仍是第一次调用时传入的props值。这样没法致使及时控制显隐。此时react的componentWillReceiveProps()出场,完美解决这个bug。
俗话说bug是解不完的,虽然上面的组件勉强能够正常使用,可是用于样式经过绝对定位来作的,无形中致使了另一个坑,若是Modal的父组件采用了相对或者绝对定位,即影响了Modal组件的定位,就会存在Modal出如今了某个div中,而不是理想的body中。bug复现以下:
git
为了保证咱们的组件始终处于body中,采起了ReactDOM中的的这个不太正式的API。语法很简单:github
ReactDOM.unstable_renderSubtreeIntoContainer(parent, component, dom)
parent通常是this,component是Modal,dom是div
代码实现以下:web
export default class extends Component { appendMaskIntoDoc() { ReactDOM.unstable_renderSubtreeIntoContainer( this, <Modal {...this.props}> {this.props.children} </Modal>, this.container ) } componentDidMount() { this.container = document.createElement('div') document.body.appendChild(this.container) this.appendMaskIntoDoc() } componentDidUpdate() { this.appendMaskIntoDoc() } componentWillUnmount() { document.body.removeChild(this.container) } render() { return null } }
此时,Modal组件已经成功作出来了。父组件能够成功调用,效果以下:
不过,偷偷see了下蚂蚁金服官网的Modal组件调用,还有一种API形式的调用。因而也简单实现了下。这里,简单说下实现思路吧。
Confirm function内部经过setState方法函数接受的参数传递给Modal的父组件dialog,onOk的promise异步回调则是在dialog内部经过处理以后再传递给Modal组件。
此处我是经过正则表达式检测new Promise
,若是属于Promise,则给onOk绑定then,内部调用setState控制Modal的隐藏。不过在调用Confirm function以前,
dialog组件已经被render进ReactDOM中,render以前则须要dom节点,就须要能获取到document节点。而后手动建立空div节点添加到body中。
此处代码有点长,省略咯。正则表达式