如何在react中封装一个eventlistener?

488 阅读3分钟

场景

在日常的开发过程中,经常会遇到对网页进行事件监听的相关需求。如果不使用任何三方库,自己去做应该是这样子的:

const hello = () => console.log('world');

useEffect(() => {
  window.addEventListener('click', hello);

  return () => {
    window.removeEventListener('click', hello);
  };
});

需要在组件加载时,添加相关事件的监听;在组件被销毁时,卸载相关事件的监听。以此来提升相关页面的实际性能。

但是如果每个事件监听都这么做的话,会导致整体的代码冗余过多。

因此就需要对相关的代码封装hooks,方便后续的重复使用。

接下来笔者就为大家分析一下如何封装一个useEventListener的hooks,来在react中挂载相关监听事件。

实现

源码

useEventListener需要用户传入以下四个参数:

  • target:需要挂载监听事件的节点
  • eventName:需要监听的事件名,如clickhashchange
  • listener:具体执行的监听函数
  • options:监听的选项,具体内容可以参考:MDN-addEventListener

下面是该hooks的具体实现代码,代码后附有详细的解析:

export function useEventListener(target, eventName, listener, options) {
  // ①
  const storedListener = useRef(listener);

  // ②
  useEffect(() => {
    storedListener.current = listener;
  }, [listener]);

  useEffect(() => {
    // ③
    const element = getTargetElement(target, window);

    // ④
    if (!element || !element?.addEventListener) return;

    // ⑤
    const eventListener = (event: any) => storedListener.current(event);

    // ⑥
    element.addEventListener(eventName, eventListener, options);

    return () => {
      // ⑦
      element.removeEventListener(eventName, eventListener, options);
    };
  }, [eventName, target, options]);
}

①:创建了一个ref来存储用户传入的监听方法。

②:监听用户传入的监听方法,如果监听方法有改动,那么就实时更新当前存储的监听方法。

③:用户传入的需要监听的节点,可能是多种类型的,因此需要实现一个getTargetElement方法:

// 3-1
const isFunction = (value) => typeof value === 'function'
const getTargetElement = (target, defaultElement) => {
  // 3-2
  if (!target) {
    return defaultElement;
  }

  let targetElement;
  // 3-3
  if (isFunction(target)) {
    targetElement = target();
    // 3-4
  } else if ('current' in target) {
    targetElement = target.current;
  } else {
    // 3-5
    targetElement = target;
  }

  return targetElement;
}
  • 3-1:中实现了一个方法来判断当前传入的是否是一个function
  • 3-2:如果target是一个非值,那么就直接采用默认的节点,挂载监听方法
  • 3-3:如果用户传入的是一个方法,那么意味着需要执行该方法才能获取相关的节点,因此执行获取方法后返回该节点
  • 3-4:如果用户传入的是一个Ref,那么可以通过target.current来获取相关的节点
  • 3-5:默认情况下,返回传入的target节点

④:如果通过getTargetElement方法获取的节点是一个非值或者没有addEventListener方法,那么就不执行下面的操作。

⑤:包装一个事件监听方法eventListener,方便后续传入addEventListenerremoveEventListener中。

⑥:对目标的元素添加事件监听方法,并传入用户指定的options参数。

⑦:在当前hooks被卸载时,清除目标元素中的事件监听方法。

至此我们就完成了useEventListenerhooks的封装。需要事件监听时,只需要引入该钩子即可搞定所有。

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 18 天,点击查看活动详情