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