use-event-listener
A React hook which provides a simple and declarative way to add DOM event listeners with automatic cleanup.
Features
- 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
shouldInjectEvent
is true - Standard options: Full support for all
AddEventListenerOptions
(capture, once, passive, signal)
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/removeEventListener
codeReduces component complexity by abstracting event handling logic
Automatic cleanup ensures listeners are removed when:-
-> Component unmounts
->
Target
element changes->
Event
type changes-> Any of
Options
params:-shouldInjectEvent
,capture
,once
,passive
,signal
gets 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 does not return anything.
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>
)
}