用 React 的思路写弹窗组件

#1

大家是怎么做的呢?

今天写了一个简单的 Toast 组件,贴出来,互相交流… 还有许多完善的地方

CSS NEXT

.toast {
  position: fixed;
  width: 320px;
  padding: 1em 1.5em;
  margin-left: -160px;
  border-radius: var(--radius);
  text-align: center;
  color: #fff;
  font-size: 14px;
}

.cancel {
  position: absolute;
  top: .25em;
  right: .25em;
  color: #fff;
  cursor: pointer;
}

.center {
  top: 50%;
  left: 50%;
  margin-top: -100px;
}

.top {
  top: 80px;
  left: 50%;
}

.right {
  right: 20px;
  top: 80px;
  margin-left: 0;
}

.bottom {
  bottom: 70px;
  left: 50%;
}

.default {
  background-color: color(#000 alpha(80%));
}

.success {
  background-color: color(var(--success) alpha(90%));
}

.danger {
  background-color: color(var(--failed) alpha(90%));
}

Javascript

/**
*
* Toast
*
*/

import React, { Component } from 'react'
import { v4 } from 'uuid'

import Cancel from 'react-icons/lib/md/cancel'
import styles from './styles.css'

let baseIndex = 12

class Toast extends Component {
  constructor(props) {
    super(props)

    this.state = {
      toasts: [],
      zindex: baseIndex,
    }

    this._add = this._add.bind(this)
    this._remove = this._remove.bind(this)
    this._clearOnExpire = this._clearOnExpire.bind(this)
    this._renderToasts = this._renderToasts.bind(this)
    this._renderToastItem = this._renderToastItem.bind(this)
  }

  componentWillUnmount() {
    const toasts = this.state.toasts
    for (var i = 0; i < toasts.length; i++) {
      clearTimeout(this[toasts[i].id])
    }
  }
  
  _add(msg = '提示不能为空', {expire = 5000, type = 'default', position = 'top', style={}}) {
    const zindex = this.state.zindex + 1
    const message = {
      id: v4(),
      msg: msg,
      type: type,
      position: position,
      zindex: zindex,
      style: style,
      expire: expire, 
    }
    this.setState({
      toasts: this.state.toasts.concat(message),
      zindex: zindex,
    }, () => this._clearOnExpire(message.id, expire))

    return message.id
  }

  _remove(id) {
    if(!this[id]) return
    if(this.state.toasts.length === 1) {
      this.setState({
        zindex: baseIndex,
      })
    }
    clearTimeout(this[id])
    delete this[id]
    this.setState({
      toasts: this.state.toasts.filter((item) => item.id !== id)
    })
  }

  _clearOnExpire(id, expire) {
    this[id] = setTimeout(() => {
      this._remove(id)
    }, expire)
  }
  
  _renderToastItem(item, index) {
    return (
      <div 
        key={item.id}
        style={{
          ...item.style,
          zIndex: item.zindex
        }}
        className={`${styles.toast} ${styles[item.type]} ${styles[item.position]}`}
      >
        <Cancel
          onClick={() => this._remove(item.id)}
          className={styles.cancel}
        />
        {item.msg}
      </div>
    )
  }

  _renderToasts() {
    return this.state.toasts.map((item, index) => this._renderToastItem(item, index))
  }

  render() {
    return (
      <div>
        { this._renderToasts() }
      </div>
    )
  }
}

export default Toast

只是简单的 toast ,展示思路…

#2

然后使用的话,在路由根组件包含进去:


<Toast ref={c => window.Toast = c} />  // 这里我直接赋予为全局的

以后不管在哪个页面都可以:

Toast._add('msg', {position: 'right'})
#3

借用了你的方法,谢谢

#4

最近也使用react.js开发了一个功能丰富的PC端弹窗组件。
https://www.cnblogs.com/xiaoyan2017/p/14085142.html

2 Likes
#5

https://mcui.fekit.cn/#route=plugins/react/react-layer 我也开发了一个图层客理插件,可弹一切东东。Toast,Popup,Sidebar都不在话下,关键是隔空传送不用组件之间通信,再乱的组件层级都怕。