use-event-listener.ts 2.4 KB

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