useEventListener()

Use EventListener with simplicity by React Hook.

Supports Window, Element and Document and custom events with almost the same parameters as the native addEventListener options. See examples below.

The Hook

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

Usage

1import { useRef } from 'react'
2
3import { useEventListener } from 'usehooks-ts'
4
5export default function Component() {
6 // Define button ref
7 const buttonRef = useRef<HTMLButtonElement>(null)
8 const documentRef = useRef<Document>(document)
9
10 const onScroll = (event: Event) => {
11 console.log('window scrolled!', event)
12 }
13
14 const onClick = (event: Event) => {
15 console.log('button clicked!', event)
16 }
17
18 const onVisibilityChange = (event: Event) => {
19 console.log('doc visibility changed!', {
20 isVisible: !document.hidden,
21 event,
22 })
23 }
24
25 // example with window based event
26 useEventListener('scroll', onScroll)
27
28 // example with document based event
29 useEventListener('visibilitychange', onVisibilityChange, documentRef)
30
31 // example with element based event
32 useEventListener('click', onClick, buttonRef)
33
34 return (
35 <div style={{ minHeight: '200vh' }}>
36 <button ref={buttonRef}>Click me</button>
37 </div>
38 )
39}

Edit on CodeSandbox

See a way to make this page better?
Edit there »