import { useEffect, useRef } from "react"; import type { RefObject } from "react"; import { useIsomorphicLayoutEffect } from "./useIsomorphicLayoutEffect"; // MediaQueryList Event based useEventListener interface function useEventListener( eventName: K, handler: (event: MediaQueryListEventMap[K]) => void, element: RefObject, options?: boolean | AddEventListenerOptions, ): void; // Window Event based useEventListener interface function useEventListener( eventName: K, handler: (event: WindowEventMap[K]) => void, element?: undefined, options?: boolean | AddEventListenerOptions, ): void; // Element Event based useEventListener interface function useEventListener< K extends keyof HTMLElementEventMap & keyof SVGElementEventMap, T extends Element = K extends keyof HTMLElementEventMap ? HTMLDivElement : SVGElement, >( eventName: K, handler: ((event: HTMLElementEventMap[K]) => void) | ((event: SVGElementEventMap[K]) => void), element: RefObject, options?: boolean | AddEventListenerOptions, ): void; // Document Event based useEventListener interface function useEventListener( eventName: K, handler: (event: DocumentEventMap[K]) => void, element: RefObject, 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, options?: boolean | AddEventListenerOptions, ) { // Create a ref that stores handler const savedHandler = useRef(handler); useIsomorphicLayoutEffect(() => { savedHandler.current = handler; }, [handler]); useEffect(() => { // Define the listening target const targetElement: T | Window = element?.current ?? window; if (!(targetElement && targetElement.addEventListener)) return; // Create event listener that calls handler function stored in ref const listener: typeof handler = (event) => { savedHandler.current(event); }; targetElement.addEventListener(eventName, listener, options); // Remove event listener on cleanup return () => { targetElement.removeEventListener(eventName, listener, options); }; }, [eventName, element, options]); } export { useEventListener };