1 | import { RefObject, useEffect, useRef, useLayoutEffect } from 'react';
|
---|
2 |
|
---|
3 | // ----------------------------------------------------------------------
|
---|
4 |
|
---|
5 | const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
|
---|
6 |
|
---|
7 | // Window Event based useEventListener interface
|
---|
8 | export function useEventListener<K extends keyof WindowEventMap>(
|
---|
9 | eventName: K,
|
---|
10 | handler: (event: WindowEventMap[K]) => void,
|
---|
11 | element?: undefined,
|
---|
12 | options?: boolean | AddEventListenerOptions
|
---|
13 | ): void;
|
---|
14 |
|
---|
15 | // Element Event based useEventListener interface
|
---|
16 | export function useEventListener<
|
---|
17 | K extends keyof HTMLElementEventMap,
|
---|
18 | T extends HTMLElement = HTMLDivElement,
|
---|
19 | >(
|
---|
20 | eventName: K,
|
---|
21 | handler: (event: HTMLElementEventMap[K]) => void,
|
---|
22 | element: RefObject<T>,
|
---|
23 | options?: boolean | AddEventListenerOptions
|
---|
24 | ): void;
|
---|
25 |
|
---|
26 | // Document Event based useEventListener interface
|
---|
27 | export function useEventListener<K extends keyof DocumentEventMap>(
|
---|
28 | eventName: K,
|
---|
29 | handler: (event: DocumentEventMap[K]) => void,
|
---|
30 | element: RefObject<Document>,
|
---|
31 | options?: boolean | AddEventListenerOptions
|
---|
32 | ): void;
|
---|
33 |
|
---|
34 | export function useEventListener<
|
---|
35 | KW extends keyof WindowEventMap,
|
---|
36 | KH extends keyof HTMLElementEventMap,
|
---|
37 | T extends HTMLElement | void = void,
|
---|
38 | >(
|
---|
39 | eventName: KW | KH,
|
---|
40 | handler: (event: WindowEventMap[KW] | HTMLElementEventMap[KH] | Event) => void,
|
---|
41 | element?: RefObject<T>,
|
---|
42 | options?: boolean | AddEventListenerOptions
|
---|
43 | ) {
|
---|
44 | // Create a ref that stores handler
|
---|
45 | const savedHandler = useRef(handler);
|
---|
46 |
|
---|
47 | useIsomorphicLayoutEffect(() => {
|
---|
48 | savedHandler.current = handler;
|
---|
49 | }, [handler]);
|
---|
50 |
|
---|
51 | useEffect(() => {
|
---|
52 | // Define the listening target
|
---|
53 | const targetElement: T | Window = element?.current || window;
|
---|
54 | if (!(targetElement && targetElement.addEventListener)) {
|
---|
55 | return;
|
---|
56 | }
|
---|
57 |
|
---|
58 | // Create event listener that calls handler function stored in ref
|
---|
59 | const eventListener: typeof handler = (event) => savedHandler.current(event);
|
---|
60 |
|
---|
61 | targetElement.addEventListener(eventName, eventListener, options);
|
---|
62 |
|
---|
63 | // Remove event listener on cleanup
|
---|
64 | // eslint-disable-next-line consistent-return
|
---|
65 | return () => {
|
---|
66 | targetElement.removeEventListener(eventName, eventListener);
|
---|
67 | };
|
---|
68 | }, [eventName, element, options]);
|
---|
69 | }
|
---|