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 相关文章推荐
利用Ext Js生成动态树实例代码
Sep 08 Javascript
在页面中js获取光标/鼠标的坐标及光标的像素坐标
Nov 11 Javascript
js控制input输入字符解析
Dec 27 Javascript
js查找某元素中的所有图片地址的方法
Jan 16 Javascript
vue实现添加标签demo示例代码
Jan 21 Javascript
js es6系列教程 - 基于new.target属性与es5改造es6的类语法
Sep 02 Javascript
vue 指定组件缓存实例详解
Apr 01 Javascript
vue-router中的hash和history两种模式的区别
Jul 17 Javascript
vue js秒转天数小时分钟秒的实例代码
Aug 08 Javascript
详解VUE Element-UI多级菜单动态渲染的组件
Apr 25 Javascript
jquery插件开发模式实例详解
Jul 20 jQuery
vue-cli3 热更新配置操作
Sep 18 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 smarty模版引擎中的缓存应用
2009/12/02 PHP
Apache环境下PHP利用HTTP缓存协议原理解析及应用分析
2010/02/16 PHP
PHP无限分类(树形类)
2013/09/28 PHP
简单说说PHP优化那些事(经验分享)
2014/11/27 PHP
PHP整合PayPal支付
2015/06/11 PHP
PHP封装的page分页类定义与用法完整示例
2018/12/24 PHP
基于jQuery的树控件实现代码(asp.net+json)
2010/07/11 Javascript
jQuery 一个图片切换的插件
2011/10/09 Javascript
jquery 漂亮的删除确认和提交无刷新删除示例
2013/11/13 Javascript
Node.js插件的正确编写方式
2014/08/03 Javascript
javascript记录文本框内文字个数检测文字个数变化
2014/10/14 Javascript
bootstrap table 服务器端分页例子分享
2015/02/10 Javascript
酷炫jQuery全屏3D焦点图动画效果
2016/03/22 Javascript
BootStrap学习笔记之nav导航栏和面包屑导航
2017/01/03 Javascript
JS获取当前地理位置的方法
2017/10/25 Javascript
react-router4按需加载(踩坑填坑)
2019/01/06 Javascript
使用webpack4编译并压缩ES6代码的方法示例
2019/04/24 Javascript
nodejs简单抓包工具使用详解
2019/08/23 NodeJs
node.js express框架实现文件上传与下载功能实例详解
2019/10/15 Javascript
jQuery实现图片切换效果
2020/10/19 jQuery
Python修改Excel数据的实例代码
2013/11/01 Python
Python MySQLdb Linux下安装笔记
2015/05/09 Python
用PyInstaller把Python代码打包成单个独立的exe可执行文件
2018/05/26 Python
Python实现调用另一个路径下py文件中的函数方法总结
2018/06/07 Python
python utc datetime转换为时间戳的方法
2019/01/15 Python
Python实现微信消息防撤回功能的实例代码
2019/04/29 Python
python实现日志按天分割
2019/07/22 Python
Python写捕鱼达人的游戏实现
2020/03/31 Python
python实现处理mysql结果输出方式
2020/04/09 Python
《蝙蝠和雷达》教学反思
2014/04/23 职场文书
国庆节演讲稿范文2014
2014/09/19 职场文书
2014年重阳节老干部座谈会局领导发言稿
2014/09/25 职场文书
2015社区爱国卫生工作总结
2015/04/21 职场文书
2015年纪委工作总结
2015/05/13 职场文书
Nginx反向代理多个服务器的实现方法
2021/03/31 Servers
Pycharm 如何设置HTML文件自动补全代码或标签
2021/05/21 Python