在React项目中如果涉及到在多个组件中进行通信,可以使用这个hooks
使用方式
tsx
// 初始化
const event = useEventEmitter();
// 发送一个事件
event.emit("hello");
// 订阅通知, 当调用emit函数时,所有订阅的位置都会收到通知
event.useSubscription(val => {
console.log(val);
});
源码
源码的核心是一个发布订阅类EventEmitter
,里面包括一个订阅的Set集合subscriptions
,发送方法emit
和订阅方法useSubscription
,底部是一个封装好的初始化hooksuseEventEmitter
:
tsx
import { useRef, useEffect } from 'react';
type Subscription<T> = (val: T) => void;
export class EventEmitter<T> {
private subscriptions = new Set<Subscription<T>>();
emit = (val: T) => {
for (const subscription of this.subscriptions) {
subscription(val);
}
};
useSubscription = (callback: Subscription<T>) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const callbackRef = useRef<Subscription<T>>(undefined);
callbackRef.current = callback;
// eslint-disable-next-line react-hooks/rules-of-hooks
useEffect(() => {
function subscription(val: T) {
if (callbackRef.current) {
callbackRef.current(val);
}
}
this.subscriptions.add(subscription);
return () => {
this.subscriptions.delete(subscription);
};
}, []);
};
}
function useEventEmitter<T = void>() {
const ref = useRef<EventEmitter<T>>(undefined);
if (!ref.current) {
ref.current = new EventEmitter();
}
return ref.current;
}
export default useEventEmitter;
subscriptions
用于保存所有已被订阅的事件,使用Set集合可以保证所有方法在全局只被注册一次
emit
用于发送事件通知,当调用emit
方法时,会遍历subscriptions
集合,通知所有已订阅的事件
useSubscription
用于订阅事件的hooks,内部使用useRef
接收一个传入的事件,目的是为了保证每次在useEffect
中调用的callback是最新的,避免闭包陷进
内部使用useEffect
的原因是为了保证每个订阅的事件只在组件首次挂载时订阅一次,在组件卸载时取消订阅,这样获得更好的性能
useEventEmitter
最后是底部的useEventEmitter
,它的作用是初始化EventEmitter类且返回一个该类的实例,使用useRef
的目的是为了保证在调用该hooks的组件中,EventEmitter
类只实例化一次