视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
React实现全局组件的Toast轻提示效果
2020-11-27 22:07:07 责编:小采
文档


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()

    代码实现

    目录结构:

    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;base,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAM1BMVEUAAAD///////////////////////////////////////////////////////////////+3leKCAAAAEHRSTlMAENCA8KAgsGDgQMCQUDBwhylaLQAAAL1JREFUOMu9U0kSwyAMK9jsS/T/1zZt2pgEZzq9RBeMZYRGDI/70bO5JptjrOAQVTonIJVK5bW2ma9A7VvpK8OdeQfbZectrDnyU+Oo0b68wGK0muDPdxpOciaizq5pkAgiIPAoew2rBVNYZoM2YHbZDNKz/2Ogam3ff5gMEL8wisfh2KKZiFiGWFkk1B7NSbhNQFy4M2+PghbODNsg7y8THM2njiy8gBgcaEUA9GgNJbxh6fJv+NxiFvYmPAFtCQZNK1qZIAAAAABJRU5ErkJggg==") no-repeat;
     background-size: 100%; }
     .toast_success {
     background: url("data:image/png;base,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAM1BMVEUAAAD///////////////////////////////////////////////////////////////+3leKCAAAAEHRSTlMA8DAQ0GBP4LCggMBwIJBAIttdjAAAAINJREFUOMvdkUsOwyAMBbH5hUCauf9pK1SlohF438x2LPn52f09+8vUfiNb/gighj8FouEjYCUoQDXiBSD7pdcMiK7XC9wCFmlDO3T20Scgx287ne13pwDUNOJ3g3maCmJDANqIGRtLj8oi1ed1GMdmcB7wXIYX8QdQZJiM5Em3smbyVICDCOrCqSAAAAAElFTkSuQmCC") no-repeat;
     background-size: 100%; }
     .toast_error {
     background: url("data:image/png;base,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;base,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); } }

    下载本文
    显示全文
    专题