All hooks / Events and input
useEventListener
Adds an event listener and removes it on unmount, like addEventListener in an effect, but always calls the latest handler without re-subscribing.
Pass 'window' or 'document' as strings (safe on the server), an element, a ref object, or null to not listen. Re-subscribes only when the target, type, or options change.
Import
import { useEventListener } from '@vincent.ri/react-tils'
// or only this hook
import { useEventListener } from '@vincent.ri/react-tils/useEventListener'Usage
useEventListener('window', 'message', (event) => {
if (event.origin === 'https://widget.example.com') setData(event.data)
})
const ref = useRef<HTMLDivElement>(null)
useEventListener(ref, 'pointermove', (event) => setX(event.clientX), { passive: true })Live demo
Press any key.
When to use it
No dedicated hook covers the event, e.g. keydown for a game, message from an iframe, or pointermove while dragging.
When not to
A dedicated hook exists (useWindowSize, useOnline, useHotkey, useClickOutside, ...); they also give a server value and avoid re-renders. For element events in your own JSX, use props like onClick.
API
function useEventListener<T extends EventListenerTarget, K extends (keyof EventMapFor<T> & string) | (string & {})>(target: T, type: K, handler: (event: EventFor<T, K>) => void, options?: Pick<AddEventListenerOptions, "capture" | "passive" | "once">): void| Parameter | Description |
|---|---|
target | 'window', 'document', an EventTarget, a ref, or null. |
type | The event name, e.g. 'keydown', or a custom event name. |
handler | Called with the event. Can change every render. |
options | capture, passive, once. Same as addEventListener. |
Types
/** Where to listen: `'window'` and `'document'` are strings so they are safe to pass during server render. */
export type EventListenerTarget = "window" | "document" | EventTarget | RefObject<EventTarget | null> | null | undefined;
/** The event map for a target, so `type` autocompletes and the handler gets the right event type. */
export type EventMapFor<T> = T extends "window" ? WindowEventMap : T extends "document" ? DocumentEventMap : T extends HTMLElement | RefObject<HTMLElement | null> ? HTMLElementEventMap : Record<string, Event>;
/** The event type for a name: typed for known events, plain `Event` for custom ones. */
export type EventFor<T, K> = K extends keyof EventMapFor<T> ? EventMapFor<T>[K] : Event;Browser support
Uses only long-established browser features, so it works wherever React does.
First version with support, from MDN's compatibility data (web-features 3.42.0, @mdn/browser-compat-data 8.1.5).