React彈窗組件

原文地址 小寒的博客react

這裏的彈窗泛指全部的彈出組件,這些組件不受頁面其餘UI佈局影響,處於DOM結構的頂層,絕對定位在body元素下。算法

 

這個特殊性也給它的開發提出了特殊的要求。api

 

react新版本中的createPortal Api能夠很方便的製造一個組件到制定的dom裏。app

在componentDidMount中進行ReactDOM.render方法是一個很巧妙的技巧。dom

 

話很少說,開始貼代碼佈局

 

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的時候,卸載彈窗this

而更新屬性也是經過從新render去渲染的,因爲react偉大的diff算法,咱們即便ReactDOM.render從新渲染也不會致使頁面刷新,而只是屬性的變化引發的頁面變更spa

 

2. createElementToModalRoot方法code

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的過程

相關文章
相關標籤/搜索