Skip to content

useEventListener

作用

优雅的使用 addEventListener

原理

通过 useEffectWithTarget 来实现 addEventListenerremoveEventListener

源码

ts
import useLatest from '../useLatest'
import type { BasicTarget } from '../utils/domTarget'
import { getTargetElement } from '../utils/domTarget'
import useEffectWithTarget from '../utils/useEffectWithTarget'

type noop = (...p: any) => void

//  定义事件的类型
export type Target = BasicTarget<HTMLElement | Element | Window | Document>

// 定义事件的配置参数类型
type Options<T extends Target = Target> = {
  target?: T
  capture?: boolean
  once?: boolean
  passive?: boolean
}

// 通过函数重载来实现不同类型的事件
function useEventListener<K extends keyof HTMLElementEventMap>(
  eventName: K,
  handler: (ev: HTMLElementEventMap[K]) => void,
  options?: Options<HTMLElement>
): void
function useEventListener<K extends keyof ElementEventMap>(
  eventName: K,
  handler: (ev: ElementEventMap[K]) => void,
  options?: Options<Element>
): void
function useEventListener<K extends keyof DocumentEventMap>(
  eventName: K,
  handler: (ev: DocumentEventMap[K]) => void,
  options?: Options<Document>
): void
function useEventListener<K extends keyof WindowEventMap>(
  eventName: K,
  handler: (ev: WindowEventMap[K]) => void,
  options?: Options<Window>
): void
function useEventListener(eventName: string, handler: noop, options: Options): void

function useEventListener(eventName: string, handler: noop, options: Options = {}) {
  // 通过 useLatest 来保存 handler 的引用
  const handlerRef = useLatest(handler)

  // 通过 useEffectWithTarget 来实现 addEventListener 和 removeEventListener
  useEffectWithTarget(
    () => {
      // 通过 getTargetElement 来获取 targetElement
      const targetElement = getTargetElement(options.target, window)
      // 如果 targetElement 不存在或者不支持 addEventListener 则直接返回
      if (!targetElement?.addEventListener) {
        return
      }
      // 通过 handlerRef.current 来获取最新的 handler
      const eventListener = (event: Event) => {
        return handlerRef.current(event)
      }

      // 添加事件监听
      targetElement.addEventListener(eventName, eventListener, {
        capture: options.capture,
        once: options.once,
        passive: options.passive
      })

      // 组件卸载的时候移除事件监听
      return () => {
        targetElement.removeEventListener(eventName, eventListener, {
          capture: options.capture
        })
      }
    },
    // 通过 deps 来控制是否重新绑定事件
    [eventName, options.capture, options.once, options.passive],
    // 通过 target 来控制事件绑定的目标
    options.target
  )
}

export default useEventListener

如有转载或 CV 的请标注本站原文地址