React + 发布订阅 ===> Modal弹窗

  • 移动端的 modal 弹窗 在项目开发中 非常的常见,各种组件库也有此功能。但是它究竟是怎样的方式实现的呢,今天让我们一起来探究探究

先看看实现效果

在这里插入图片描述
要造个轮子之前,我们需要先分析步骤,而后才开始动手写代码,实现具体细节。

  1. 弹窗 是 一个组件 ,暂且取名叫 BaseModal 。在页面引入 BaseModal 组件。
  2. 点击按钮,弹出弹窗( 命令式 调用 ), BaseModal.show({ option })
  3. option 是要配置的各种参数,比如 弹窗标题、弹窗内容、弹窗按钮文案等等
  4. 点击弹窗上的确定、取消按钮,关闭弹窗。
  • 以上就是 实现弹窗的 逻辑分析,接下来我们按照上面的步骤来写出代码逻辑

一、BaseModal 组件

// 最初始的 一个 ui 组件,这里的css 样式在后面
const BaseModal = () => {
  return (
    <div className={`${styles.container}`}>
      <div className={`${styles.inner}`}>
        <div className={styles.top}>
        <div className={styles.title}>我是标题</div>
          <div className={styles.content}>我是内容</div>
        </div>
        <div className={`${styles.footer} ${styles.footerNormal}`}>
          <div  className={`${styles.public} ${styles.cancel}`}>
            取消
          </div>
          <div className={`${styles.public} ${styles.sure}`}>
            确定
          </div>
         </div>
      </div>
    </div>
  )
}

export default BaseModal

效果图
在这里插入图片描述

二、命令式调用 BaseModal.show() 弹出弹窗

import reactDom from 'react-dom/client'

const BaseModal = () => {
  // ... 同上
}

// 新加show 方法,动态的向body 里面插入标签
const show = () => {
  const div = document.createElement('div')
  const root = reactDom.createRoot(div)
  document.body.append(div)
  root.render(<BaseModal />)
}

BaseModal.show = show
export default BaseModal

三、配置 option 参数


export interface OptionsType {
  /** 提示标题 */
  title?: string
  /** 提示的内容 */
  content?: React.ReactNode
  /** 是否显示取消按钮 @default true */
  showCancel?: boolean
  /** 取消按钮文字 @default "取消" */
  cancelText?: string
  /** 确定按钮文字 @default "确认" */
  confirmText?: string
  /** 点击取消按钮 */
  onCancel?: () => void
  /** 点击确定按钮 */
  onConfirm?: (_?: any) => void
}

const BaseModal = (props: OptionsType) => {
  const { title, content } = props
  return (
    <div className={`${styles.container}`}>
      <div className={`${styles.inner}`}>
        <div className={styles.top}>
        	// 更改成动态传入
          { title && <div className={styles.title}>{ title }</div> }
          { content && <div className={styles.content}>{ content }</div> }
        </div>
        <div className={`${styles.footer} ${styles.footerNormal}`}>
          <div  className={`${styles.public} ${styles.cancel}`}>
            取消
          </div>
          <div className={`${styles.public} ${styles.sure}`}>
            确定
          </div>
          </div>
      </div>
    </div>
  )
}

// 新增入参 option 
const show = (option: OptionsType) => {
  const div = document.createElement('div')
  const root = reactDom.createRoot(div)
  document.body.append(div)
  // 把参数 option 传给 BaseModal 组件
  root.render(<BaseModal {...option} />)
}

BaseModal.show = show
export default BaseModal

/**
 * 调用 BaseModal1.show({ title: '我是传入标题', content: '我是传入内容' })
*/ 

四、点击 确认 、取消 按钮,关闭弹窗

import reactDom from 'react-dom/client'
import events from './events'

export interface OptionsType {
  /** 提示标题 */
  title?: string
  /** 提示的内容 */
  content?: React.ReactNode
  /** 是否显示取消按钮 @default true */
  showCancel?: boolean
  /** 取消按钮文字 @default "取消" */
  cancelText?: string
  /** 确定按钮文字 @default "确认" */
  confirmText?: string
  /** 点击取消按钮 */
  onCancel?: () => void
  /** 点击确定按钮 */
  onConfirm?: (_?: any) => void
}

/**
 * @author czj
 * @description modal弹窗
*/
const BaseModal = (props: OptionsType) => {
  const { title, content,} = props

  const clickCancel = () => {
    events.emit('closeModal', 'cancel')
  }

  const clickSure = () => {
    events.emit('closeModal', 'confirm')
  }

  return (
    <div className={`${styles.container}`}>
      <div className={`${styles.inner}`}>
        <div className={styles.top}>
          { title && <div className={styles.title}>{ title }</div> }
          { content && <div className={styles.content}>{ content }</div> }
        </div>
        <div className={`${styles.footer} ${styles.footerNormal}`}>
          <div  className={`${styles.public} ${styles.cancel}`} onClick={clickCancel}>
            取消
          </div>
          <div className={`${styles.public} ${styles.sure}`} onClick={clickSure}>
            确定
          </div>
          </div>
      </div>
    </div>
  )
}

const show = (option: OptionsType) => {
  const div = document.createElement('div')
  const root = reactDom.createRoot(div)
  document.body.append(div)
  root.render(<BaseModal {...option} />)

  // 订阅 一个 关闭弹窗事件
  events.on('closeModal', (type: string) => {
    // type 点击的事件源,取消按钮还是 确认按钮
    root.unmount()
    document.body.removeChild(div)
    // 解绑事件
    events.off('closeModal')
  })
}

BaseModal.show = show
export default BaseModal

/**
 * 调用 BaseModal1.show({ title: '我是传入标题', content: '我是传入内容' })
*/

最终效果:
在这里插入图片描述

效果优化

import reactDom from 'react-dom/client'
import styles from './index.module.scss'
import events from './events'
import { useEffect, useState } from 'react'

export interface OptionsType {
  /** 提示标题 */
  title?: string
  /** 提示的内容 */
  content?: React.ReactNode
  /** 是否显示取消按钮 @default true */
  showCancel?: boolean
  /** 取消按钮文字 @default "取消" */
  cancelText?: string
  /** 确定按钮文字 @default "确认" */
  confirmText?: string
  /** 点击取消按钮 */
  onCancel?: () => void
  /** 点击确定按钮 */
  onConfirm?: (_?: any) => void
}

/**
 * @description modal弹窗
*/
const BaseModal = (props: OptionsType) => {
  const { title, content, showCancel, confirmText, cancelText, onCancel, onConfirm } = props

  // 做动画
  const [show, setShow] = useState(false)

  /** 弹窗关闭时的动画 */
  const hiddenModal = (cb: () => void) => {
    return () => {
      setShow(false)
      //  延迟300 ms 执行后面的操作
      setTimeout(() => cb(), 400)
    }
  }

  /** 点击取消按钮 */
  const cancelClick = hiddenModal(() => {
    events.emit('closeModal', 'cancel')
    onCancel?.()
  })

  /** 点击确认按钮 */
  const confirmClick = hiddenModal(() => {
    events.emit('closeModal', 'confirm')
    onConfirm?.()
  })

  useEffect(() => {
    setTimeout(() => {
      setShow(true)
    }, 0)
  }, [])

  return (
    <div className={`${styles.container} ${show && styles.containerShow}`}>
      <div className={`${styles.inner} ${show && styles.innerShow}`}>
        <div className={styles.top}>
          {
            title && <div className={styles.title}>{title}</div>
          }
          <div className={styles.content}>{content}</div>
        </div>
   
        <div className={`${styles.footer} ${styles.footerNormal}`}>
          { showCancel
            && <div onClick={cancelClick} className={`${styles.public} ${styles.cancel}`}>
              {cancelText || '取消'}
            </div>
          }
          <div onClick={confirmClick} className={`${styles.public} ${styles.sure}`}>
            {confirmText || '确定'}
          </div>
        </div>
      </div>
    </div>
  )
}

export default BaseModal

/**
 * @description 显示出弹窗
*/
const show = (options: OptionsType) => {
  // 返回promise 
  return new Promise((resolve, reject) => {
    const div = document.createElement('div')
    const root = reactDom.createRoot(div)
    document.body.append(div)
    root.render(<BaseModal {...options} />)

    events.on('closeModal', (type) => {
      type === 'confirm' ? resolve({}) : reject()
      root.unmount()
      document.body.removeChild(div)
      // 解绑事件
      events.off('closeModal')
    })
  })
}

BaseModal.show = show
/**
 * await BaseModal1.show({ title: '我是传入标题', content: '我是传入内容' })
 * console.log('----')
*/

css

.container {
  width: 100vw;
  height: 100vh;
  position: fixed;
  z-index: 9999;
  left: 0;
  top: 0;
  background-color: rgba(0, 0, 0, 0.5);
  opacity: 0;
  transition: .4s;
}

.containerShow {
  opacity: 1;
}

.inner {
  width: 640px;
  border-radius: 16px;
  position: absolute;
  left: 50%;
  top: 50%;
  background-color: #fff;
  text-align: center;
  transform: translate(-50%,-50%) scale(0.7);
  transition: .4s;
  overflow: hidden;
}

.innerShow {
  transform:translate(-50%,-50%) scale(1);
}

.top {
  padding: 32px 24px;
}

.title {
  font-size: 18px;
  color: #323232;
  font-size: 36px;
  font-weight: bold;
}

.content {
  margin-top: 20px;
  color: rgba(0, 0, 0, 0.85);
  font-size: 32px;
}

.footer {
  width: 100%;
  display: flex;
  justify-content: space-between;
  padding: 0 32px;
  padding-bottom: 20px;

  border-radius: 8px;

  
}

.footerNormal {
  border-top: 1px solid rgba(230, 230, 230, 1);
  height: 96px;
  padding: 0;
}

.public {
  width: 200px;
  height: 96px;
  color: #323232;
  font-size: 32px;
  font-weight: bold;
  
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 1;
  background-color: #fff !important;
}

.cancel {
  background-color: #fff;
  color: rgba(50, 50, 51, 1);
  border-right: 1px solid rgba(230, 230, 230, 1);
}

.sure {
  background-color: #5290fd;
  color: rgba(82, 144, 253, 1);
}

.buttonCancel {
  margin-right: 32px;
}
  • 6
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React Modal 是一个常用的弹窗组件库,可以用来实现弹窗输入框的功能。下面是一个简单的实现示例: 首先,你需要引入 React Modal: ```javascript import Modal from 'react-modal'; ``` 然后,在你的组件中,你需要定义一个状态来控制弹窗的显示与隐藏: ```javascript function MyComponent() { const [modalIsOpen, setModalIsOpen] = useState(false); const openModal = () => { setModalIsOpen(true); }; const closeModal = () => { setModalIsOpen(false); }; return ( <div> <button onClick={openModal}>打开弹窗</button> <Modal isOpen={modalIsOpen} onRequestClose={closeModal} > <h2>请输入:</h2> <input type="text" /> <button onClick={closeModal}>确定</button> </Modal> </div> ); } ``` 在这个示例中,我们定义了一个状态 `modalIsOpen`,表示弹窗是否打开。当用户点击打开弹窗的按钮时,我们调用 `openModal` 函数来更新状态,打开弹窗。当用户点击弹窗中的确定按钮或者关闭弹窗时,我们调用 `closeModal` 函数来更新状态,关闭弹窗。 在弹窗中,我们可以通过普通的 HTML 元素来实现输入框和按钮。在这个示例中,我们定义了一个输入框和一个确定按钮,用户可以在输入框中输入内容,并点击确定按钮来关闭弹窗。 需要注意的是,在 React Modal 中,我们需要通过 `isOpen` 属性来控制弹窗的显示与隐藏。当 `isOpen` 的值为 `true` 时,弹窗会显示出来;当 `isOpen` 的值为 `false` 时,弹窗会隐藏起来。同时,我们还需要定义一个 `onRequestClose` 回调函数,来处理用户点击弹窗以外的区域时,弹窗自动关闭的情况。 希望这个示例对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值