JAVASCRIPT
How to safely attach and detach event listeners with useEventListener
Discover `useEventListener`, a custom React hook for declaratively attaching global or element-specific event listeners with proper cleanup, preventing memory leaks.
import { useRef, useEffect } from 'react';
function useEventListener(eventName, handler, element = window) {
const savedHandler = useRef();
useEffect(() => {
savedHandler.current = handler;
}, [handler]);
useEffect(() => {
const isSupported = element && element.addEventListener;
if (!isSupported) return;
const eventListener = (event) => savedHandler.current(event);
element.addEventListener(eventName, eventListener);
return () => {
element.removeEventListener(eventName, eventListener);
};
}, [eventName, element]);
}
// Example Usage:
// function MyComponent() {
// const [coords, setCoords] = useState({ x: 0, y: 0 });
// const handleMouseMove = (event) => {
// setCoords({ x: event.clientX, y: event.clientY });
// };
// useEventListener('mousemove', handleMouseMove);
// return (
// <div>
// <h1>Mouse Position:</h1>
// <p>X: {coords.x}, Y: {coords.y}</p>
// </div>
// );
// }
// // Example with a specific element:
// function ButtonComponent() {
// const buttonRef = useRef(null);
// const [clicks, setClicks] = useState(0);
// const handleClick = () => {
// setClicks(c => c + 1);
// };
// useEventListener('click', handleClick, buttonRef.current);
// return (
// <button ref={buttonRef}>Clicked {clicks} times</button>
// );
// }
How it works: The `useEventListener` hook simplifies the process of attaching and detaching event listeners to the `window` object or a specified DOM element. It ensures that the event listener is correctly added when the component mounts and removed when it unmounts, preventing memory leaks. By storing the handler in a `useRef`, it avoids issues with stale closures and ensures the latest handler is always called, even if its dependencies change.