React实现全局组件的Toast轻提示效果


Posted in Javascript onSeptember 21, 2018

Toast是常用的轻提示弹框,常用于页面loading和提示语弹窗。

本例基于React实现一个随时可调用且不随页面渲染的全局组件。

需求分析

  • Toast 不需要同页面一起被渲染,而是根据需要被随时调用。
  • Toast 是一个轻量级的提示组件,它的提示不会打断用户操作,并且会在提示的一段时间后自动关闭。
  • Toast 需要提供几种不同的消息类型以适应不同的使用场景。
  • Toast 的方法必须足够简洁,以避免不必要的代码冗余。

如何使用

首先引入

import Toast from './components/toast'

JSX中事件调用:

<button onClick={() => { Toast.info('普通提示') }}>普通提示</button>

JS中方法调用:

Toast.info('普通提示')

回调方法:

const hideLoading = Toast.loading('加载中...', 0, () => {
  Toast.success('加载完成')
})
setTimeout(hideLoading, 2000)

调用规则:

3个参数:

  • content 提示内容 string(loading方法为可选)
  • duration 提示持续时间 number,单位ms(可选)
  • onClose 提示关闭时的回调函数(可选)
Toast.info("普通",2000)
Toast.success("成功",1000,() => {
  console.log('回调方法')
}))
Toast.error("错误")
Toast.loading()

代码实现

目录结构:

React实现全局组件的Toast轻提示效果

  1. index.js:对外export接口,设置默认的参数值,全局创建或销毁Toast的DIV。
  2. toast.js:Toast具体显示的内容及多次调用Toast时的状态管理。
  3. toast.css:Toast的样式,费话不多说。

index.js:

import React from 'react'
import ReactDOM from 'react-dom'
import Toast from './toast'
import './toast.css'

function createNotification() {
  const div = document.createElement('div')
  document.body.appendChild(div)
  const notification = ReactDOM.render(<Toast />, div)
  return {
    addNotice(notice) {
      return notification.addNotice(notice)
    },
    destroy() {
      ReactDOM.unmountComponentAtNode(div)
      document.body.removeChild(div)
    }
  }
}

let notification
const notice = (type, content, duration = 2000, onClose) => {
  if (!notification) notification = createNotification()
  return notification.addNotice({ type, content, duration, onClose })
}

export default {
  info(content, duration, onClose) {
    return notice('info', content, duration, onClose)
  },
  success(content = '操作成功', duration, onClose) {
    return notice('success', content, duration, onClose)
  },
  error(content, duration , onClose) {
    return notice('error', content, duration, onClose)
  },
  loading(content = '加载中...', duration = 0, onClose) {
    return notice('loading', content, duration, onClose)
  }
}

toast.js:

import React, { Component } from 'react'

class ToastBox extends Component {
  constructor() {
    super()
    this.transitionTime = 300
    this.state = { notices: [] }
    this.removeNotice = this.removeNotice.bind(this)
  }

  getNoticeKey() {
    const { notices } = this.state
    return `notice-${new Date().getTime()}-${notices.length}`
  }

  addNotice(notice) {
    const { notices } = this.state
    notice.key = this.getNoticeKey()

    // notices.push(notice);//展示所有的提示
    notices[0] = notice;//仅展示最后一个提示
    
    this.setState({ notices })
    if (notice.duration > 0) {
      setTimeout(() => {
        this.removeNotice(notice.key)
      }, notice.duration)
    }
    return () => { this.removeNotice(notice.key) }
  }

  removeNotice(key) {
    const { notices } = this.state
    this.setState({
      notices: notices.filter((notice) => {
        if (notice.key === key) {
          if (notice.onClose) setTimeout(notice.onClose, this.transitionTime)
          return false
        }
        return true
      })
    })
  }

  render() {
    const { notices } = this.state
    const icons = {
      info: 'toast_info',
      success: 'toast_success',
      error: 'toast_error',
      loading: 'toast_loading'
    }
    return (
      <div className="toast">
        {
          notices.map(notice => (
            <div className="toast_bg" key={notice.key}>
              <div className='toast_box'>
                <div className={`toast_icon ${icons[notice.type]}`}></div>
                <div className='toast_text'>{notice.content}</div> 
              </div>
            </div>
          ))
        }
      </div>
    )
  }
}

export default ToastBox

toast.css:

.toast {
 position: fixed;
 left: 0;
 top: 0;
 z-index: 999;
 display: flex;
 flex-direction: column; }
 .toast_bg {
  position: fixed;
  width: 100%;
  height: 100%;
  left: 0;
  top: 0; }
 .toast_box {
  position: relative;
  left: 50%;
  top: 50%;
  width: 2.8rem;
  height: 2rem;
  margin: -1rem -1.4rem;
  background: rgba(0, 0, 0, 0.65);
  border-radius: .1rem;
  color: #fff; }
 .toast_text {
  position: absolute;
  bottom: 16%;
  text-align: center;
  width: 90%;
  margin: 0 5%;
  height: .28rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap; }
 .toast_icon {
  position: relative;
  left: 50%;
  top: 15%;
  margin: -.4rem;
  width: .8rem;
  height: .8rem; }
 .toast_loading {
  -webkit-animation: loading 1s steps(12, end) infinite;
  animation: loading 1s steps(12, end) infinite;
  background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAM1BMVEUAAAD///////////////////////////////////////////////////////////////+3leKCAAAAEHRSTlMAENCA8KAgsGDgQMCQUDBwhylaLQAAAL1JREFUOMu9U0kSwyAMK9jsS/T/1zZt2pgEZzq9RBeMZYRGDI/70bO5JptjrOAQVTonIJVK5bW2ma9A7VvpK8OdeQfbZectrDnyU+Oo0b68wGK0muDPdxpOciaizq5pkAgiIPAoew2rBVNYZoM2YHbZDNKz/2Ogam3ff5gMEL8wisfh2KKZiFiGWFkk1B7NSbhNQFy4M2+PghbODNsg7y8THM2njiy8gBgcaEUA9GgNJbxh6fJv+NxiFvYmPAFtCQZNK1qZIAAAAABJRU5ErkJggg==") no-repeat;
  background-size: 100%; }
 .toast_success {
  background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAM1BMVEUAAAD///////////////////////////////////////////////////////////////+3leKCAAAAEHRSTlMA8DAQ0GBP4LCggMBwIJBAIttdjAAAAINJREFUOMvdkUsOwyAMBbH5hUCauf9pK1SlohF438x2LPn52f09+8vUfiNb/gighj8FouEjYCUoQDXiBSD7pdcMiK7XC9wCFmlDO3T20Scgx287ne13pwDU89NOJ3g3maCmJDANqIGRtLj8oi1ed1GMdmcB7wXIYX8QdQZJiM5Em3smbyVICDCOrCqSAAAAAElFTkSuQmCC") no-repeat;
  background-size: 100%; }
 .toast_error {
  background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgBAMAAACBVGfHAAAAMFBMVEUAAAD///////////////////////////////////////////////////////////87TQQwAAAAD3RSTlMA0BDAMODwUKBgsCCAQJClzVPvAAAA0UlEQVQoz2MgErAclv9o44Dgc8b/B4KvBTA+t/3XdgeWivjPG6ACbl8ngNXlp0AN+L8IwtD6DzFm2w+Y3v5sMGW/ACbA9Rms9ZsCTIApH2QR608GhoUKQJ4xA8P8AKCAP5CwF2JgUPwIlPwCFDj/AMRRYJIHCnL8AZkJ1AfkAcUYGNhBpso7MICUgBQw8H4EEv/B5ssDFYA4mAKYWjANfd+Aai3CYZ9BDoM63RDkdEGQ0zE9h+l9zADCDEIGt2/wQEZEwwVepGhgYEdEFGZUEgYAW05XI3jSsVwAAAAASUVORK5CYII=") no-repeat;
  background-size: 100%; }
 .toast_info {
  background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgBAMAAACBVGfHAAAALVBMVEUAAAD///////////////////////////////////////////////////////+hSKubAAAADnRSTlMA4CCAwKBAMJBg8NAQUNhWlbcAAAC+SURBVCjPYyASsLfse+1cgOBzyr0DgocXYHwmv4dtCkwZck8UoAJZDydA1C2H8NnexUAYR99BjNF6CtMbtwhM+QUACUUhIMH6BKz14QEgafcYSPDIgSxifMkAE2CYJwAk6gQQAozPgURfA0KAA0T6JSAE2ECm7lNACDC9BhLvGGACIA6GAFyLohBEC9xQqLeeQKwFA4i1EIfBAeNzuNMVhSBOx/AcpvcxAwgzCDEDGTMaGHhhEYWIShN4VBIGAPvRT5YzufhUAAAAAElFTkSuQmCC") no-repeat;
  background-size: 100%; }

@-webkit-keyframes loading {
 0% {
  -webkit-transform: rotate3d(0, 0, 1, 0deg);
  transform: rotate3d(0, 0, 1, 0deg); }
 100% {
  -webkit-transform: rotate3d(0, 0, 1, 360deg);
  transform: rotate3d(0, 0, 1, 360deg); } }

@keyframes loading {
 0% {
  -webkit-transform: rotate3d(0, 0, 1, 0deg);
  transform: rotate3d(0, 0, 1, 0deg); }
 100% {
  -webkit-transform: rotate3d(0, 0, 1, 360deg);
  transform: rotate3d(0, 0, 1, 360deg); } }

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript的对话框详解与参数
Mar 08 Javascript
JS无法捕获滚动条上的mouse up事件的原因猜想
Mar 21 Javascript
Javascript 键盘事件的组合使用实现代码
May 04 Javascript
js实现拖拽效果
Feb 12 Javascript
jquery利用json实现页面之间传值的实例解析
Dec 12 Javascript
微信小程序 开发之快递查询功能的实现
Jan 09 Javascript
微信小程序 基础知识css样式media标签
Feb 15 Javascript
微信小程序日历组件calendar详解及实例
Jun 08 Javascript
jQuery实现可兼容IE6的滚动监听功能
Sep 20 jQuery
基于vue.js实现的分页
Mar 13 Javascript
js实现动态增加文件域表单功能
Oct 22 Javascript
Vue表单绑定的实例代码(单选按钮,选择框(单选时,多选时,用 v-for 渲染的动态选项)
May 13 Javascript
vue中进入详情页记住滚动位置的方法(keep-alive)
Sep 21 #Javascript
vue 使某个组件不被 keep-alive 缓存的方法
Sep 21 #Javascript
解决vue keep-alive 数据更新的问题
Sep 21 #Javascript
vue axios基于常见业务场景的二次封装的实现
Sep 21 #Javascript
vue2使用keep-alive缓存多层列表页的方法
Sep 21 #Javascript
使用ng-packagr打包Angular的方法示例
Sep 21 #Javascript
基于vue中keep-alive缓存问题的解决方法
Sep 21 #Javascript
You might like
php ci 获取表单中多个同名input元素值的代码
2016/03/25 PHP
PHP数组去重的更快实现方式分析
2018/05/09 PHP
THINKPHP5.1 Config的配置与获取详解
2020/06/08 PHP
jquery 图片预加载 自动等比例缩放插件
2008/12/25 Javascript
DOM 基本方法
2009/07/18 Javascript
javascript下数值型比较难点说明
2010/06/07 Javascript
jquery实现手风琴效果
2015/11/20 Javascript
jQuery实现返回顶部功能
2016/02/23 Javascript
js 判断附件后缀的简单实现方法
2016/10/11 Javascript
vue项目中做编辑功能传递数据时遇到问题的解决方法
2016/12/19 Javascript
使用vue和datatables进行表格的服务器端分页实例代码
2017/06/07 Javascript
vue axios用法教程详解
2017/07/23 Javascript
关于JS与jQuery中的文档加载问题
2017/08/22 jQuery
对vue里函数的调用顺序介绍
2018/03/17 Javascript
vue实现局部刷新的实现示例
2019/04/16 Javascript
js实现简单掷骰子小游戏
2019/10/24 Javascript
vue实现修改图片后实时更新
2019/11/14 Javascript
2020京东618叠蛋糕js脚本(亲测好用)
2020/06/02 Javascript
[01:04]DOTA2:伟大的Roshan雕塑震撼来临
2015/01/30 DOTA
[03:10]超级美酒第四天 fy拉比克秀 大合集
2018/06/05 DOTA
在Python的Django框架中调用方法和处理无效变量
2015/07/15 Python
Python实现树莓派WiFi断线自动重连的实例代码
2017/03/16 Python
Python实现简单求解给定整数的质因数算法示例
2018/03/25 Python
Tensorflow加载预训练模型和保存模型的实例
2018/07/27 Python
Python sqlite3查询操作过程解析
2020/02/20 Python
Pycharm配置lua编译环境过程图解
2020/11/28 Python
如何在网站上添加谷歌定位信息
2016/04/16 HTML / CSS
德国香水、化妆品和护理产品网上商店:Parfumdreams
2018/09/26 全球购物
shell变量的作用空间是什么
2013/08/17 面试题
双十佳事迹材料
2014/01/29 职场文书
青年文明号复核材料
2014/02/11 职场文书
班组建设经验交流材料
2014/05/12 职场文书
学习三严三实对照检查材料思想汇报
2014/09/22 职场文书
离婚协议书标准格式
2014/10/04 职场文书
聊聊Lombok中的@Builder注解使用教程
2021/11/17 Java/Android
前端canvas中物体边框和控制点的实现示例
2022/08/05 Javascript