React弹窗组件
原文地址 小寒的博客 这里的弹窗泛指所有的弹出组件,这些组件不受页面其他UI布局影响,处于DOM结构的顶层,绝对定位在body元素下。 ? 这个特殊性也给它的开发提出了特殊的要求。 ? react新版本中的createPortal Api可以很方便的制造一个组件到制定的dom里。 在componentDidMount中进行ReactDOM.render方法是一个很巧妙的技巧。 ? 话不多说,开始贴代码 ? 1. 在componentDidMount去渲染dom class Modal extends React.Component { el = null componentDidMount() { this.el = createElementToModalRoot() renderModal(this.el,this.props) } componentDidUpdate() { renderModal(this.el,this.props) if (!this.props.visible) { unRenderModal(this.el) } } componentWillUnMount() { unRenderModal(this.el) } render() { return null } } ? 上边的代码就是弹窗的核心思想,首先创建element在root元素下,然后渲染dom,在unmount或者visible属性为false的时候,卸载弹窗 而更新属性也是通过重新render去渲染的,由于react伟大的diff算法,我们即使ReactDOM.render重新渲染也不会导致页面刷新,而只是属性的变化引起的页面变动 ? 2.?createElementToModalRoot方法 function createElementToModalRoot() { let modalRoot modalRoot = document.getElementById(‘modal-root‘) if (!modalRoot) { modalRoot = document.createElement(‘div‘) modalRoot.setAttribute(‘id‘,‘modal-root‘) const modalMask = document.createElement(‘div‘) modalMask.setAttribute(‘id‘,‘modal-mask‘) document.body.appendChild(modalRoot) modalRoot.appendChild(modalMask) } const el = document.createElement(‘div‘) modalRoot.appendChild(el); return el } ? 用dom方法创建元素,因为此时componentDidMount所以我们可以肆无忌惮的进行dom操作了,执行这个方法之后我们会创建#modal-root #modal-mask 以及 待会render的dom元素 ? 3. renderModal方法 const renderModal = (el,props) => { const modalRoot = document.getElementById(‘modal-root‘) const modalMask = document.getElementById(‘modal-mask‘) modalMask.style.display = ‘block‘ modalRoot.style.display = ‘block‘ ReactDOM.render(<ModalInner {...props} />, el) } ? 上面添的代码我们用ModalInner组件创建了一个去渲染了添加在#modal-root下面的dom,每次更新组件,也是通过他再次渲染 ? 4. ModalInner组件 class ModalInner extends React.Component { render() { const { children,title,visible,onCancel,onOk } = this.props return ( <div className={classnames(‘modal‘,visible ? ‘modal-animate-in‘ : ‘modal-animate-out‘)}> <div className="modal-head"> <div className="modal-title">{title}</div> <div className="modal-cancel-btn" onClick={onCancel}>+</div> </div> <div className="modal-content"> {children} </div> <div className="modal-footer"> <button className="do-btn" onClick={onCancel}>取消</button> <button className="do-btn do-btn-primary" onClick={onOk}>确定</button> </div> </div> ) } } ? 这个组件,我们设置了最常用的一些属性,包括title children visible 和 onCancel onOk ? 5. unRenderModal方法 最后我们就剩下卸载方法了 const unRenderModal = (el) => { const modalRoot = document.getElementById(‘modal-root‘) const modalMask = document.getElementById(‘modal-mask‘) modalMask.style.display = ‘none‘ modalRoot.style.display = ‘none‘ modalRoot.removeChild(el); } ? ? ? 6. 添加动画上边的ModalInner组件里可以看到他会根据visible对dom添加不同的animate从而产生动画 但是如果unRenderModal方法会直接移除dom,所以不会产生移除动画 ? 所以我们把上边的componentDidMount修改一下 componentDidUpdate() { renderModal(this.el,this.props) if (!this.props.visible) { setTimeout(() => unRenderModal(this.el),500) } } ? ? 7. Modal.open方法 Modal.open = option => { const props = {...option} const el = createElementToModalRoot() const close = () => { option.visible = false renderModal(el,option) setTimeout(() => unRenderModal(el),500) } props.visible = true props.children = option.content props.onOk = e => { option.onOk ? option.onOk(e,close) : close() } props.onCancel = () => { option.ononCancel ? option.ononCancel(e,close) : close() } renderModal(el,props) } ? 还是用的上面的那些api,这是visible属性是我们手动传入组件里的 这样我们就可以通过非api的形式去打开一个弹窗了 ? ? 以上便是render方法创建弹窗的方式,当然很推荐使用createPortal方法,可以省去手动render和unRender的过程 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |