最近组内有一个新项目,须要用的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