react toast自定义组件

2,471 阅读4分钟

最近一段时间开始研究react,对于一个小白来说一个新的框架要学很多东西,不过还好,基本mvvm框架大体都差不多,只是使用上语法有所区别(当然不讨论底层逻辑),但是还是遇到了不少值得自己总结的东西,这次主要说下toast自定义组件。ps:完整代码在最后,可以直接跳过过程查看代码~

在vue中我们定义toast可以定义一个组件,然后写一个install方法转换成插件模式,就可以直接引用了,但是react貌似没有这种方法呢,但是万变不离其宗,它还是一个组件。

考虑下toast组件都有哪些功能,无非就是一些轻提示、loading等效果,可以在不同的时候调用不同的方法展示不同的内容。有了这个大方向,我们考虑最终使用toast采用如下方式:

Toast.success('成功‘)
Toast.error('失败’, 3000)
Toast.loading(true)
Toast.loading(false)

针对不容状态使用不同方法,参数分别为展示文字内容、展示时间、是否显示loading等,其中对于状态提示的两个参数均为可选,而loading方法是否展示为必填参数。同时我们将toast组件挂载到页面一个动态生成div里,直接通过css进行控制显示隐藏。

话不多说,直接上代码,以success为例:

import React, {Fragment} from 'react'
import ReactDom from 'react-dom'

//此处为toast展示的对应图片
import success from './images/success.png'

export default class Toast extends React.Component {
    constructor(props){
        super(props)
    }
    static success(mes="success", duration=2500){
        ReactDOM.render(
            <Fragment>
                <div className="toast">
                    <div className="toast-box">
                        <img src={success} className="toast-icon" />
                        <span>{mes}</span>
                    </div>
                </div>
            </Fragment>,
            document.getElementById('toast')
        )
    }
}

好了,基本的组件有了,但是我们发现他是静态的,而且页面上没有id=“toast”这个html元素,因此我们需要添加方法去让它动起来~

//这里我们添加两个方法,一个是初始化状态方法,一个是设置定时器方法

let timer = null;
//初始化方法
function init(){
    clearTimeout(timer)
    let toast = document.getElementById('toast')
    if(toast){
        toast.style.display = 'block'
    }else{
        let div = document.createElement('div')
        div.setAttribute('id', 'toast')
        document.body.appendChild(div)
    }
}
//设置定时器
function setTimer(duration){
    timer = setTimeout(()=>{
        let toast = document.getElementById('toast')
        if(toast){
            toast.style.display = 'none'
        }
    }, duration)
}

//同时我们在组件的success方法里调用这两个方法
...省略内容
    static success(mes="success", duration=2500){
        init()
        setTimer(duration)
        ...省略内容
    }
...省略内容

好了,一个success toast就完成了,我们可以看下效果,点击按钮时调用Toast.sucess('领取失败')(为了查看效果,我临时添加了背景色)


这时我们发现,效果是正常的,但是貌似右侧出现了不河蟹的滚动条,这也是我们经常遇到的触发弹层禁止底部页面滚动问题,再添加对应方法消灭它

//添加两个方法,当组件触发时,对body添加类名同时保存滚动高度;组件隐藏时,删除类恢复滚动位置
let _scrollTop = 0
function popupOpen(){
    _scrollTop = document.scrollingElement.scrollTop
    document.body.style.top = -_scrollTop + 'px'
    codument.body.classList.add('body-popup')
}
function popupClose(){
    document.scrollingElement.scrollTop = _scrollTop
    document.body.classList.remove('body-popup')
}

添加了两个方法后,效果perfect~~~

同样的道理,添加error和loading方法,注意loading方法需要有个status去判断是显示还是隐藏。完整代码奉上:

//toast.jsx
import React, { Fragment } from 'react'
import ReactDOM from 'react-dom'
import success from '../../../assets/images/toast_success.png'
import error from '../../../assets/images/toast_warning.png'
import loading from '../../../assets/images/loading.gif'
import './toast.scss'

let timer = null
let _scrollTop = 0
export default class Toast extends React.Component{    
    constructor(props){
        super(props)
    }
    static success(mes='success', duration = 2500){
        init()
        setTime(duration)
        ReactDOM.render(
            <Fragment>
                <div className="toast">
                    <div className="toast-box"> 
                       <img className="toast-icon" src={success} alt=""/>
                        <span>{mes}</span>
                    </div>
                </div>
            </Fragment>,
            document.getElementById('toast')
        )
    }
    static error(mes='fail', duration = 2500){
        init()
        setTime(duration)
        ReactDOM.render(
            <Fragment>
                <div className="toast">
                    <div className="toast-box">
                        <img className="toast-icon" src={error} alt=""/>
                        <span>{mes}</span>
                    </div>
                </div>
            </Fragment>,
            document.getElementById('toast')
        )
    }
    static loading(status, mes=''){
        init()
        setLoading(status)
        ReactDOM.render(
            <Fragment>
                <div className="toast">
                    <div className="toast-box">
                        <img className="toast-icon" src={loading} alt=""/>
                        <span>{mes}</span>
                    </div>
                </div>
            </Fragment>,
            document.getElementById('toast')
        )
    }
}

//弹层出现时设置body类名,防止页面滚动
function popupOpen(){
    _scrollTop = document.scrollingElement.scrollTop
    document.body.classList.add('body-popup')
    document.body.style.top = - _scrollTop + 'px'
}
//弹层隐藏时恢复滚动
function popupClose(){
    document.body.classList.remove('body-popup')
    document.scrollingElement.scrollTop = _scrollTop
}
//设置loading
function setLoading(status){
    let toast = document.getElementById('toast')
    if(status){
        toast.style.display = 'block'
        popupOpen()
    }else{
        toast.style.display = 'none'
        popupClose()
    }
}
//初始化toast
function init(){
    clearTimeout(timer)
    let toast = document.getElementById('toast')
    if(toast){
        toast.style.display = 'block'
    }else{ 
       let div = document.createElement('div')
        div.setAttribute('id', 'toast')
        document.body.appendChild(div)
    }
    popupOpen()
}
//设置定时器
function setTime(duration){
    timer = setTimeout(()=>{
        let toast = document.getElementById('toast')
        if(toast){
            toast.style.display = 'none'
            popupClose()
        }
    }, duration)
}

//toast.scss
#toast{
    position: relative;
    z-index: 999;
}
.toast{
    position: fixed;
    left: 0;
    top: 0;
    bottom: 0;
    right: 0;
    .toast-box{
        width: 240px;
        height: 240px;
        background: rgba(0, 0, 0, .6);
        position: absolute;
        left: 50%;
        top: 50%;
        margin: -120px 0 0 -120px;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        border-radius: 10px;
        .toast-icon{
            width: 100px;
            height: 100px; 
           margin-bottom: 20px;
        }
        span{
            font-size: 28px;
            color: #fff;
        }
    }
}

//body的类方法
body.body-popup{
    position: fixed;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    overflow: hidden;
}

码字不易,对自己做个记录,同时希望对大家有所帮助~有不足之处欢迎指出~