Back
Frontend

Event listener hook for react

1 minute read
#React

Events can be very hard to work with in react so i made a type safe hook that simplifies the process.

The last thing i want is to download a whole hook library just for one hook so the simplest thing is to add it yourself.

typescript

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

Usage:

tsx

1useEventListener('paste', handlePaste, codesRef);

Martin Kulvedrøsten Myhre

Fullstack developer & junior consultant at Inmeta Consoulting AS. CEO and founder of Limeyfy AS and 3Steps AS.