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// Window Event based useEventListener interface
6function useEventListener<K extends keyof WindowEventMap>(
7 eventName: K,
8 handler: (event: WindowEventMap[K]) => void,
9 element?: undefined,
10 options?: boolean | AddEventListenerOptions,
11): void
12
13// Element Event based useEventListener interface
14function useEventListener<
15 K extends keyof HTMLElementEventMap,
16 T extends HTMLElement = HTMLDivElement,
17>(
18 eventName: K,
19 handler: (event: HTMLElementEventMap[K]) => void,
20 element: RefObject<T>,
21 options?: boolean | AddEventListenerOptions,
22): void
23
24// Document Event based useEventListener interface
25function useEventListener<K extends keyof DocumentEventMap>(
26 eventName: K,
27 handler: (event: DocumentEventMap[K]) => void,
28 element: RefObject<Document>,
29 options?: boolean | AddEventListenerOptions,
30): void
31
32function useEventListener<
33 KW extends keyof WindowEventMap,
34 KH extends keyof HTMLElementEventMap,
35 T extends HTMLElement | void = void,
36>(
37 eventName: KW | KH,
38 handler: (
39 event: WindowEventMap[KW] | HTMLElementEventMap[KH] | Event,
40 ) => 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 return () => {
65 targetElement.removeEventListener(eventName, eventListener)
66 }
67 }, [eventName, element, options])
68}
69
70export 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 »