Skip to content

useEventListener

Category
Export Size
781 B
Last Changed
3 days ago

Use EventListener with ease. Register using addEventListener on mounted, and removeEventListener automatically on unmounted

Demo

Usage

tsx
import { 
useEventListener
} from '@reause/core'
import {
useRef
} from 'react'
const
button
=
useRef
<HTMLButtonElement>(null)
useEventListener
(
button
, 'click', (
evt
) => {
console
.
log
(
evt
)
})

Every target is a React ref, and the listener is typed by that target's own event map — evt above is a PointerEvent.

Default Target

When the target is omitted, it defaults to window:

tsx
import { 
useEventListener
} from '@reause/core'
// Listens on window
useEventListener
('resize', (
evt
) => {
console
.
log
(
evt
)
})

Reactive Target

You can pass a ref as the event target, useEventListener will unregister the previous event and register the new one when the target changes:

tsx
import { 
useEventListener
} from '@reause/core'
import {
useRef
} from 'react'
const
element
=
useRef
<HTMLDivElement>(null)
useEventListener
(
element
, 'keydown', (
e
) => {
console
.
log
(
e
.
key
)
})

Multiple Events

You can pass an array of events to listen to multiple events at once:

tsx
const 
element
=
useRef
<HTMLDivElement>(null)
useEventListener
(
element
, ['mouseenter', 'mouseleave'], (
evt
) => {
console
.
log
(
evt
.
type
)
})

Multiple Targets

You can also pass an array of targets:

tsx
const 
first
=
useRef
<HTMLButtonElement>(null)
const
second
=
useRef
<HTMLButtonElement>(null)
useEventListener
([
first
,
second
], 'click', (
evt
) => {
console
.
log
('Button clicked')
})

Cleanup

Returns a cleanup function to manually unregister the listener:

tsx
const 
element
=
useRef
<HTMLDivElement>(null)
const
cleanup
=
useEventListener
(
element
, 'keydown', (
e
) => {
console
.
log
(
e
.
key
)
})
cleanup
() // This will unregister the listeners.

useEventListener is SSR-safe: nothing touches window during render, and binding happens in the mount effect.

Type Declarations

Toggle
ts
export type WindowEventName = keyof WindowEventMap

export type DocumentEventName = keyof DocumentEventMap

export type ShadowRootEventName = keyof ShadowRootEventMap

export interface GeneralEventListener<E = Event> {
    (evt: E): void;
}

export type EventTargetRef<T> = RefObject<T | null | undefined>

export type EventTargetRefs<T> = EventTargetRef<T> | RefObject<T[] | null | undefined> | EventTargetRef<T>[]

type Arrayable<T> = T | T[]

type Fn = () => void

type EventMapOfElement<T> = T extends HTMLVideoElement ? HTMLVideoElementEventMap : T extends HTMLMediaElement ? HTMLMediaElementEventMap : T extends HTMLBodyElement ? HTMLBodyElementEventMap : T extends HTMLFrameSetElement ? HTMLFrameSetElementEventMap : T extends SVGSVGElement ? SVGSVGElementEventMap : T extends SVGElement ? SVGElementEventMap : T extends MathMLElement ? MathMLElementEventMap : HTMLElementEventMap

interface InferEventTarget<Events> {
    addEventListener: (event: Events, fn?: any, options?: any) => any;
    removeEventListener: (event: Events, fn?: any, options?: any) => any;
}

export function useEventListener<E extends keyof WindowEventMap>(event: Arrayable<E>, listener: Arrayable<(this: Window, ev: WindowEventMap[E]) => any>, options?: boolean | AddEventListenerOptions): Fn

export function useEventListener<E extends keyof WindowEventMap>(target: EventTargetRefs<Window>, event: Arrayable<E>, listener: Arrayable<(this: Window, ev: WindowEventMap[E]) => any>, options?: boolean | AddEventListenerOptions): Fn

export function useEventListener<E extends keyof DocumentEventMap>(target: EventTargetRefs<Document>, event: Arrayable<E>, listener: Arrayable<(this: Document, ev: DocumentEventMap[E]) => any>, options?: boolean | AddEventListenerOptions): Fn

export function useEventListener<E extends keyof ShadowRootEventMap>(target: EventTargetRefs<ShadowRoot>, event: Arrayable<E>, listener: Arrayable<(this: ShadowRoot, ev: ShadowRootEventMap[E]) => any>, options?: boolean | AddEventListenerOptions): Fn

export function useEventListener<T extends Element, E extends keyof EventMapOfElement<T>>(target: EventTargetRefs<T>, event: Arrayable<E>, listener: Arrayable<GeneralEventListener<EventMapOfElement<T>[E]>>, options?: boolean | AddEventListenerOptions): Fn

export function useEventListener<Names extends string, EventType = Event>(target: EventTargetRefs<InferEventTarget<Names>>, event: Arrayable<Names>, listener: Arrayable<GeneralEventListener<EventType>>, options?: boolean | AddEventListenerOptions): Fn

export function useEventListener<EventType = Event>(target: EventTargetRefs<EventTarget>, event: Arrayable<string>, listener: Arrayable<GeneralEventListener<EventType>>, options?: boolean | AddEventListenerOptions): Fn

Source

Source · Demo · VueUse

Contributors

hairyf

Changelog

v0.1.8 on
9456b - fix(core): type useEventListener element targets by their own event map
v0.1.7 on
a82a7 - fix(core): resolve DOM ref targets after commit
v0.1.0 on
6b3ed - fix(core): useEventListener re-bind must skip inline listeners (#553)
8334a - fix(core): resolve useEventListener audit findings (#553)
8fddc - chore!: remove all Vue-only Maybe* types and getter unions, adopt React Ref (#462)

Released under the MIT License. v0.1.8