useEventListener
A declarative way to attach event listeners to the window or a specific DOM element.
Code
1import { useRef, useEffect } from "react"
2
3type EventListener = <K extends keyof WindowEventMap>(
4 eventName: K,
5 handler: (event: WindowEventMap[K]) => void,
6 element?: HTMLElement | Window,
7) => void
8
9export const useEventListener: EventListener = (eventName, handler, element = window) => {
10 const savedHandler = useRef(handler)
11
12 useEffect(() => {
13 savedHandler.current = handler
14 }, [handler])
15
16 useEffect(() => {
17 const isSupported = element && element.addEventListener
18 if (!isSupported) return
19
20 const eventListener = (event: Event) => savedHandler.current(event as any)
21
22 element.addEventListener(eventName, eventListener)
23
24 return () => {
25 element.removeEventListener(eventName, eventListener)
26 }
27 }, [eventName, element])
28}
Usage Example
import { useState, useRef } from "react"
import { useEventListener } from "@/hooks/use-event-listener"
import { Button } from "@/components/ui/button"
import { Card, CardContent } from "@/components/ui/card"
export function EventListenerExample() {
const [coords, setCoords] = useState({ x: 0, y: 0 })
const boxRef = useRef(null)
const handleMouseMove = (event: MouseEvent) => {
setCoords({ x: event.clientX, y: event.clientY })
}
// Listen to mousemove on the window
useEventListener("mousemove", handleMouseMove)
// Listen to click on a specific element
const [clickCount, setClickCount] = useState(0)
const handleClick = () => setClickCount(prev => prev + 1)
useEventListener("click", handleClick, boxRef.current || undefined)
return (
<div className="space-y-4">
<Card className="p-4">
<CardContent className="p-0">
<p>Mouse position (window): X: {coords.x}, Y: {coords.y}</p>
</CardContent>
</Card>
<Card ref={boxRef} className="p-4 cursor-pointer bg-npui-secondary hover:bg-npui-secondary/80">
<CardContent className="p-0">
<p>Click me! (Clicks: {clickCount})</p>
</CardContent>
</Card>
</div>
)
}