Toast.info('普通提示')
Toast.success('成功提示', 3000)
Toast.warning('警告提示', 1000)
Toast.error('错误提示', 2000, () => {
Toast.info('哈哈')
})
const hideLoading = Toast.loading('加载中...', 0, () => {
Toast.success('加载完成')
})
setTimeout(hideLoading, 2000)
复制代码
Toast 组件能够被分为三个部分,分别为:css
项目目录结构以下:react
├── toast
│ ├── icons.js
│ ├── index.js
│ ├── notice.js
│ ├── notification.js
│ ├── toast.css
│ ├── toast.js
复制代码
为了便于理解,这里从外部的 toast 部分开始实现。git
由于页面中没有 Toast 组件相关的元素,为了在页面中插入提示信息,即 Notice 组件,须要首先将 Notice 组件的容器 Notification 组件插入到页面中。这里定义一个 createNotification 函数,用于在页面中渲染 Notification 组件,并保留 addNotice 与 destroy 函数。github
function createNotification() {
const div = document.createElement('div')
document.body.appendChild(div)
const notification = ReactDOM.render(<Notification />, div) return { addNotice(notice) { return notification.addNotice(notice) }, destroy() { ReactDOM.unmountComponentAtNode(div) document.body.removeChild(div) } } } 复制代码
接着定义一个全局的 notification 变量用于保存 createNotification 返回的对象。并定义对外暴露的函数,这些函数被调用时就会将参数传递回 Notification 组件。由于一个页面中只须要存在一个 Notification 组件,因此每次调用函数时只须要判断当前 notification 对象是否存在便可,无需重复建立。数组
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)
},
warning(content, duration, onClose) {
return notice('warning', content, duration, onClose)
},
error(content, duration, onClose) {
return notice('error', content, duration, onClose)
},
loading(content, duration = 0, onClose) {
return notice('loading', content, duration, onClose)
}
}
复制代码
这样外部工做就已经完成,接着须要完成 Notification 组件内部的实现。首先 Notification 组件的 state 属性中有一个 notices 属性,用于保存当前页面中存在的 Notice 的信息。而且 Notification 组件拥有 addNotice 和 removeNotice 两个方法,用于向 notices 中添加和移除 Notice 的信息(下文简写为 notice)。bash
添加 notice 时,须要使用 getNoticeKey 方法为这个 notice 添加惟一的key值,再将其添加到 notices 中。并根据参数提供的 duration,设置定时器以在到达时间后将其自动关闭,这里规定若 duration 的值小于等于0则消息不会自动关闭,而是一直显示。最后方法返回移除自身 notice 的方法给调用者,以便其根据须要当即关闭这条提示。app
调用 removeNotice 方法时,会根据传递的key的值遍历 notices,若是找到结果,就触发其回调函数并从 notices 中移除。ide
最后就是遍历 notices 数组并将 notice 属性传递给 Notice 组件以完成渲染,这里使用 react-transition-group 实现组件的进场与出场动画。svg
(注:关于页面中同时存在多条提示时的显示问题,本文中采用的方案时直接将后一条提示替换掉前一条消息,因此代码中添加 notice 直接写成了 notices[0] = notice 而非 notices.push(notice), 若是想要页面中多条提示共存的效果能够自行修改。)函数
class Notification 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()
if (notices.every(item => item.key !== notice.key)) {
notices[0] = notice
this.setState({ notices })
if (notice.duration > 0) {
setTimeout(() => {
this.removeNotice(notice.key)
}, notice.duration)
}
}
return () => { this.removeNotice(notice.key) }
}
removeNotice(key) {
this.setState(previousState => ({
notices: previousState.notices.filter((notice) => {
if (notice.key === key) {
if (notice.onClose) notice.onClose()
return false
}
return true
})
}))
}
render() {
const { notices } = this.state
return (
<TransitionGroup className="toast-notification"> { notices.map(notice => ( <CSSTransition key={notice.key} classNames="toast-notice-wrapper notice" timeout={this.transitionTime} > <Notice {...notice} /> </CSSTransition> )) } </TransitionGroup> ) } } 复制代码
最后剩下的 Notice 组件就很简单了,只须要根据 Notification 组件传递的信息输出最终的内容便可。能够自行发挥设计样式。
class Notice extends Component {
render() {
const icons = {
info: 'icon-info-circle-fill',
success: 'icon-check-circle-fill',
warning: 'icon-warning-circle-fill',
error: 'icon-close-circle-fill',
loading: 'icon-loading'
}
const { type, content } = this.props
return (
<div className={`toast-notice ${type}`}> <svg className="icon" aria-hidden="true"> <use xlinkHref={`#${icons[type]}`} /> </svg> <span>{content}</span> </div> ) } } 复制代码
注:主要改动为 notification.js 文件中的 addNotice 和 removeNotice 方法。原文中的代码未做修改,修改后的代码请参见 项目源码。