use-event-listener
A React hook which provides a simple and declarative way to add DOM event listeners with automatic cleanup.
Features
- Flexible target observing: Observe element with
targetprop or use setter functionsetElementRef - Auto cleanup: Automatic cleanup of events on unmount and dependency change
- Reactive: Potentially re-attaches listeners on dependency change(target, event, options)
- Conditional event: Conditional event support with feature flag. And listeners only get attached when:- target exists, handler is provided, and
shouldInjectEventis true - Standard options: Full support for all
AddEventListenerOptions(capture, once, passive, signal)
Usage Note
- Do not pass
targetprop if usingsetElementRefand vise-versa.
Problem It Solves
Boilerplate Reduction
- Problem: Manually managing event listeners in React components leads to verbose, repetitive and error-prone code with potential memory leaks.
// ❌ Problematic approach which is redundant and verbose
function Component() {
const [scrollY, setScrollY] = useState(0)
useEffect(() => {
const handleScroll = () => {
setScrollY(window.scrollY)
}
window.addEventListener('scroll', handleScroll)
return () => window.removeEventListener('scroll', handleScroll) // Doing proper cleanup on unmount
}, [])
return <div>Current: {scrollY}</div>
}Solution:
Eliminates repetitive
addEventListener/removeEventListenercodeReduces component complexity by abstracting event handling logic
Automatic cleanup ensures listeners are removed when:-
-> Component unmounts
->
Targetelement changes->
Eventtype changes-> Any of
Optionsparams:-shouldInjectEvent,capture,once,passive,signalgets changed
// ✅ Clean, declarative approach
function Component() {
const [scrollY, setScrollY] = useState(0)
const breakpoint = useEventListener({
target: () => window,
event: 'scroll',
handler: () => {
setScrollY(window.scrollY)
},
})
return <div>Current: {scrollY}</div>
}Performance Benefits
Stable references accross re-renders which prevents listeners from being repeatedly added/removed
Efficient dependency tracking
Parameters
| Parameter | Type | Required | Default Value | Description |
|---|---|---|---|---|
| target | EvTarget | ✅ | - | Target element on which the event is listened to. |
| event | string | ✅ | - | Event name (e.g. 'click', 'keydown') |
| handler | EvHandler | ❌ | undefined | Event listener callback function |
| options | EvOptions | ❌ | undefined | Event listener options and feature flags |
Options Parameter
The options parameter accepts an object that extends the standard AddEventListenerOptions with an additional custom property for conditional event handling.
Standard AddEventListenerOptions
| Property | Type | Default | Description |
|---|---|---|---|
| capture | boolean | false | If true, the listener will be triggered during the capture phase |
| once | boolean | false | If true, the listener will be automatically removed after being triggered once |
| passive | boolean | false | If true, indicates that the function will never call preventDefault() |
| signal | AbortSignal | undefined | An AbortSignal that can be used to remove the event listener |
Custom Options
| Property | Type | Default | Description |
|---|---|---|---|
| shouldInjectEvent | boolean | any | true | Controls whether the event listener should be attached. When false, the event listener is not added |
Type Definitions
Details
export type EvTarget = () => EventTarget | null
export type EvHandler = (event: Event) => void
export interface EvOptions extends AddEventListenerOptions {
// Standard AddEventListenerOptions:
capture?: boolean
once?: boolean
passive?: boolean
signal?: AbortSignal
// Custom option:
shouldInjectEvent?: boolean | any // Controls whether the event should be attached
}Return Value(s)
This hook returns an object containing the setter function for observing the target element with ref attribute
| Property | Type | Description |
|---|---|---|
| setElementRef | Function | A ref callback to observe the target element for event listening. Does not change across re-renders. |
Return Types
Details
export type UseEventListenerReturnValues = {
setElementRef: (elementNode: HTMLElement | null) => void
}Common Use Cases
- Adding dom events (e.g 'click', 'keydown', 'resize', 'scroll')
Usage Examples
Basic Click Handler
import { useRef } from 'react'
import { useEventListener } from 'classic-react-hooks'
export default function ClickExample() {
const buttonRef = useRef<HTMLButtonElement>(null)
useEventListener({
target: () => buttonRef.current,
event: 'click',
handler: (e) => {
console.log('Button clicked!', e)
},
})
return <button ref={buttonRef}>Click me</button>
}Listening Window Event
Example
import { useEventListener } from 'classic-react-hooks'
export default function WindowExample() {
useEventListener({
target: () => window,
event: 'resize',
handler: (e) => {
console.log('Window resized:', window.innerWidth, window.innerHeight)
},
})
return <div>Resize the window and check console</div>
}Conditional Event Listening
Example
import { useState } from 'react'
import { useEventListener } from 'classic-react-hooks'
export default function ConditionalExample() {
const [isListening, setIsListening] = useState(true)
useEventListener({
target: () => document,
event: 'keydown',
handler: (e) => {
console.log('Key pressed:', e.key)
},
options: {
shouldInjectEvent: isListening, // Only listen when enabled
},
})
return (
<div>
<button onClick={() => setIsListening(!isListening)}>{isListening ? 'Stop' : 'Start'} Listening</button>
<p>Press any key (when listening is enabled)</p>
</div>
)
}setElementRef for Observing target
Example
import { useState } from 'react'
import { useEventListener } from 'classic-react-hooks'
export default function ConditionalExample() {
const [counter, setCounter] = useState(0)
const { setElementRef } = useEventListener({
event: 'click',
handler: () => {
console.log(counter)
},
})
return (
<div>
<button onClick={() => setCounter((c) => c + 1)}>update counter {counter}</button>
<div ref={setElementRef}>log value</div>
</div>
)
}