Skip to content

useEventListener ​

一个用于添加事件监听器的 React Hook,支持多种目标元素和事件类型。

功能特性 ​

  • ✅ 事件监听: 简化事件监听器的添加和移除
  • ✅ 多目标支持: 支持 DOM 元素、window、document 等
  • ✅ 自动清理: 组件卸载时自动移除监听器
  • ✅ 类型安全: 完整的 TypeScript 类型支持

基本用法 ​

typescript
import useEventListener from "./useEventListener";

function Component() {
  useEventListener("click", (event) => {
    console.log("Window clicked:", event);
  });

  const buttonRef = useRef(null);

  useEventListener(
    "click",
    (event) => {
      console.log("Button clicked:", event);
    },
    buttonRef
  );

  return <button ref={buttonRef}>点击我</button>;
}

兼容性 ​

  • ✅ React 16.8+
  • ✅ TypeScript 4.0+
  • ✅ 现代浏览器

实现源码 ​

查看 useEventListener 的实现
tsx
import { useEffect, useRef } from "react";
import type { RefObject } from "react";
import { useIsomorphicLayoutEffect } from "../useIsomorphicEffect";

function useEventListener<K extends keyof MediaQueryListEventMap>(
  eventName: K,
  handler: (event: MediaQueryListEventMap[K]) => void,
  element: RefObject<MediaQueryList>,
  options?: boolean | AddEventListenerOptions
): void;

function useEventListener<K extends keyof WindowEventMap>(
  eventName: K,
  handler: (event: WindowEventMap[K]) => void,
  element?: undefined,
  options?: boolean | AddEventListenerOptions
): void;

function useEventListener<
  K extends keyof HTMLElementEventMap & keyof SVGElementEventMap,
  T extends Element = K extends keyof HTMLElementEventMap
    ? HTMLDivElement
    : SVGAElement
>(
  eventName: K,
  handler:
    | ((event: HTMLElementEventMap[K]) => void)
    | ((event: SVGElementEventMap[K]) => void),
  element?: RefObject<T>,
  options?: boolean | AddEventListenerOptions
): void;

function useEventListener<K extends keyof DocumentEventMap>(
  eventName: K,
  handler: (event: DocumentEventMap[K]) => void,
  element?: undefined,
  options?: boolean | AddEventListenerOptions
): void;

function useEventListener<
  KW extends keyof WindowEventMap,
  KH extends keyof HTMLElementEventMap & keyof SVGElementEventMap,
  KM extends keyof MediaQueryListEventMap,
  T extends HTMLElement | SVGAElement | MediaQueryList = HTMLElement
>(
  eventName: KW | KH | KM,
  handler: (
    event:
      | WindowEventMap[KW]
      | HTMLElementEventMap[KH]
      | SVGElementEventMap[KH]
      | MediaQueryListEventMap[KM]
      | Event
  ) => void,
  element?: RefObject<T>,
  options?: boolean | AddEventListenerOptions
) {
  const saveHandler = useRef(handler);

  useIsomorphicLayoutEffect(() => {
    saveHandler.current = handler;
  }, [handler]);

  useEffect(() => {
    const targetElement: T | Window = element?.current ?? window;

    if (!(targetElement && targetElement.addEventListener)) {
      return;
    }

    const listener: typeof handler = (event) => {
      saveHandler.current(event);
    };

    targetElement.addEventListener(eventName, listener, options);

    return () => {
      targetElement.removeEventListener(eventName, listener, options);
    };
  }, [eventName, element, options]);
}

export default useEventListener;

在实践中理解 React,在分享中共同成长。