logoahooks dive
Advanced

useEventEmitter

创建一个事件发射器,用于在组件之间传递事件。

用法

创建一个事件发射器,用于在组件之间传递事件。

You received a message

源码

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;

解读

先看 EventEmitter 类的实现。

export class EventEmitter<T> {
  // 1. 使用 Set 存储订阅者
  private subscriptions = new Set<Subscription<T>>();

  // 2. 定义 emit 方法,用于触发事件
  emit = (val: T) => {
    // 2.1. 遍历订阅者,调用每个订阅者的回调函数
    for (const subscription of this.subscriptions) {
      subscription(val);
    }
  };

  // 3. 定义 useSubscription 方法,用于订阅事件
  useSubscription = (callback: Subscription<T>) => {
    // 3.1. 使用 useRef 存储订阅者回调函数
    // eslint-disable-next-line react-hooks/rules-of-hooks
    const callbackRef = useRef<Subscription<T>>(undefined);
    callbackRef.current = callback;
    // 3.2. 使用 useEffect 订阅事件
    // eslint-disable-next-line react-hooks/rules-of-hooks
    useEffect(() => {
      // 3.2.1. 定义订阅者回调函数
      function subscription(val: T) {
        // 3.2.2. 如果订阅者回调函数存在,则调用该回调函数
        if (callbackRef.current) {
          callbackRef.current(val);
        }
      }
      // 3.2.3. 将订阅者回调函数添加到订阅者集合中
      this.subscriptions.add(subscription);
      // 3.2.4. 返回一个清理函数,用于在组件卸载时清理订阅者
      return () => {
        this.subscriptions.delete(subscription);
      };
    }, []);
  };
}

再来看 useEventEmitter 函数的实现。

function useEventEmitter<T = void>() {
  // 1. 使用 useRef 创建一个 ref 对象,用于存储 EventEmitter 实例
  const ref = useRef<EventEmitter<T>>(undefined);
  // 2. 如果 ref 对象不存在,则创建一个 EventEmitter 实例
  if (!ref.current) {
    // 2.1. 创建一个 EventEmitter 实例
    ref.current = new EventEmitter();
  }
  // 3. 返回 EventEmitter 实例
  return ref.current;
}

其实就是一个 EventBushook 版本,在 EventBus 的基础上,增加了当组件卸载时清理订阅者的功能。以及在组件的顶层调用订阅事件 useSubscription,因为对于一个组件来说,只需要订阅一次,也就是为什么订阅函数是一个 hook,而不是一个普通的函数,仅需要在顶层调用一次 useSubscription 即可。

而当前的 EventEmitter 仅支持发布者发布唯一事件,如果需要发布多个事件,那就需要创建多个 EventEmitter 实例了。其实根据简单改造一下就能支持多事件发布订阅。

Child1
    Child2

      Last updated on

      On this page