# 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

```ts
import { useEventListener } from '@vincent.ri/react-tils'
// or only this hook
import { useEventListener } from '@vincent.ri/react-tils/useEventListener'
```

## Usage

```ts
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 })
```

## 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

```ts
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

```ts
/** 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

Works in all modern browsers.

---

220 B minified and brotlied
Source: https://github.com/vincentri/react-tils/blob/main/src/useEventListener.ts
Docs page: https://vincentri.github.io/react-tils/hooks/useEventListener/
Previous: [useDeviceOrientation](https://vincentri.github.io/react-tils/hooks/useDeviceOrientation.md)
Next: [useClickOutside](https://vincentri.github.io/react-tils/hooks/useClickOutside.md)
