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);