import React from 'react'; import useIsomorphicLayoutEffect from './use-isomorphic-layout-effect'; // MediaQueryList Event based useEventListener interface function useEventListener( eventName: K, handler: (event: MediaQueryListEventMap[K]) => void, element: React.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, T extends HTMLElement = HTMLDivElement >( eventName: K, handler: (event: HTMLElementEventMap[K]) => void, element: React.RefObject, options?: boolean | AddEventListenerOptions ): void // Document Event based useEventListener interface function useEventListener( eventName: K, handler: (event: DocumentEventMap[K]) => void, element: React.RefObject, options?: boolean | AddEventListenerOptions ): void function useEventListener< KW extends keyof WindowEventMap, KH extends keyof HTMLElementEventMap, KM extends keyof MediaQueryListEventMap, T extends HTMLElement | MediaQueryList | void = void >( eventName: KW | KH | KM, handler: ( event: | WindowEventMap[KW] | HTMLElementEventMap[KH] | MediaQueryListEventMap[KM] | Event ) => void, element?: React.RefObject, options?: boolean | AddEventListenerOptions ) { // Create a ref that stores handler const savedHandler = React.useRef(handler) useIsomorphicLayoutEffect(() => { savedHandler.current = handler }, [handler]) React.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 default useEventListener