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 方法。原文中的代碼未做修改,修改後的代碼請參見 項目源碼。