useEventListener
作用
优雅的使用 addEventListener
原理
通过
useEffectWithTarget
来实现addEventListener
和removeEventListener
源码
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