最近組內有一個新項目,須要用的Toast
這樣一個組件。內心想,這樣的組件,還不是分分鐘就搞定呀。而後一頭砸進去了開始寫。javascript
toast 的展現與否,跟展現什麼我都交給父組件去作,Toast自己只管展現就能夠了,內部須要任何別的邏輯 初版代碼以下css
import React from 'react';
import styles from './style.module.css';
const Toast = ({ content, showToast }) => {
if (!showToast) {
return null;
}
return <div className={styles.container}>{content}</div>;
};
export default Toast;
複製代碼
而後再調用的地方,發現若是我須要使用Toast
,那麼我使用的地方都得引入一下,這顯然很不友好。 而後我就將控制Toast的展現與否交由最外層的App
組件去作, 若是那裏須要使用Toast
,那麼就將handleShowToast 傳遞下去就行了 代碼以下java
import React, { useState } from 'react';
import Toast from '@components/Toast';
const App = (props) => {
const [showToast, setToastVisible] = useState(false);
const [content, setToastContent] = useState('');
const handleShowToast = (toastContent, delay) => {
setToastVisible(true);
setToastContent(toastContent);
const timer = setTimeOut(() => {
setToastVisible(false);
setToastContent('');
}, delay)
}
return (
<div>
<Toast showToast={showToast} content={content}/>
<div>
...
</div>
</div>
);
};
export default App;
複製代碼
可是這樣用起來仍是不舒服,若是頁面多了,那豈不是每一處頁面都須要這麼處理,若是層級比較深,那豈不是要一層一層的傳遞下去?react
綜上所碰到的問題,我思考了一下我想要的Toast
組件的樣子bash
整理完上面的問題,想起了觀察者模式的應用,我是否是能夠,在 Toast
自己去訂閱幾個事件,而後當我想要處理跟Toast
相關邏輯的時候我再 emit 相關事件,事情是否是就變的簡單了呢?ui
首先我須要一個事件系統,這個事件系統須要知足如下功能this
interface EventD {
list: any;
}
export default class Event implements EventD {
list = new Map();
on(event: string, callback: any) {
if (!this.list.has(event)) {
this.list.set(event, []);
}
this.list.get(event).push(callback);
return this;
}
off(event: string) {
this.list.delete(event);
return this;
}
emit(event: string, ...args: any) {
if (this.list.has(event)) {
this.list.get(event).forEach((callback: any) => setTimeout(() => callback(...args), 0));
}
}
}
複製代碼
有了事件系統,咱們再去改造如下咱們的Toastspa
import React, { useEffect, useState } from 'react';
import Event from '@lib/event';
import styles from './style.module.css';
const event = new Event();
const Toast = () => {
const [showToast, setToastVisible] = useState(false);
const [content, setToastContent] = useState('');
useEffect(() => {
event.on('showToast', (toastContent: string, delay: number = 2000) => {
setToastContent(toastContent);
setToastVisible(true);
const timer = setTimeout(() => {
clearTimeout(timer);
setToastVisible(false);
}, delay);
});
return () => {
event.off('showToast');
};
});
if (!showToast) {
return null;
}
return <div className={styles.container}>{content}</div>;
};
export default Toast;
export const showToast = (content: string, delay?: number) => event.emit('showToast', content, delay);
複製代碼
這樣咱們就能夠在APP組件內引用一次便可,若是須要展現 toast
的時候,只須要調用一下 Toast 組件暴露的 showToast 方法便可。code