import { RefObject, useEffect, useRef, useLayoutEffect } from 'react'; // ---------------------------------------------------------------------- const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect; // Window Event based useEventListener interface export function useEventListener( eventName: K, handler: (event: WindowEventMap[K]) => void, element?: undefined, options?: boolean | AddEventListenerOptions ): void; // Element Event based useEventListener interface export function useEventListener< K extends keyof HTMLElementEventMap, T extends HTMLElement = HTMLDivElement, >( eventName: K, handler: (event: HTMLElementEventMap[K]) => void, element: RefObject, options?: boolean | AddEventListenerOptions ): void; // Document Event based useEventListener interface export function useEventListener( eventName: K, handler: (event: DocumentEventMap[K]) => void, element: RefObject, options?: boolean | AddEventListenerOptions ): void; export function useEventListener< KW extends keyof WindowEventMap, KH extends keyof HTMLElementEventMap, T extends HTMLElement | void = void, >( eventName: KW | KH, handler: (event: WindowEventMap[KW] | HTMLElementEventMap[KH] | 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 eventListener: typeof handler = (event) => savedHandler.current(event); targetElement.addEventListener(eventName, eventListener, options); // Remove event listener on cleanup // eslint-disable-next-line consistent-return return () => { targetElement.removeEventListener(eventName, eventListener); }; }, [eventName, element, options]); }