加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

React弹窗组件

发布时间:2020-12-15 20:40:02 所属栏目:百科 来源:网络整理
导读:原文地址 小寒的博客 这里的弹窗泛指所有的弹出组件,这些组件不受页面其他UI布局影响,处于DOM结构的顶层,绝对定位在body元素下。 ? 这个特殊性也给它的开发提出了特殊的要求。 ? react新版本中的createPortal Api可以很方便的制造一个组件到制定的dom里。

原文地址 小寒的博客

这里的弹窗泛指所有的弹出组件,这些组件不受页面其他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的过程

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读