useEventListener

If you find yourself adding a lot of event listeners using useEffect you might consider moving that logic to a custom hook. In the recipe below we create a useEventListener hook that handles checking if addEventListener is supported, adding the event listener, and removal on cleanup.

Usage

useEventListener.ts
import { useRef, useEffect, RefObject } from 'react';

type EventHandler<T extends Event> = (event: T) => void;

const useEventListener = <T extends Event>(
  eventName: string,
  handler: EventHandler<T>,
  element: RefObject<Element> = document
) => {
  const savedHandler = useRef<EventHandler<T>>();

  useEffect(() => {
    savedHandler.current = handler;
  }, [handler]);

  useEffect(() => {
    const isSupported = element.current && element.current.addEventListener;
    if (!isSupported) return;

    const eventListener: EventHandler<T> = (event: T) => savedHandler.current?.(event);
    element.current.addEventListener(eventName, eventListener);

    return () => {
      element.current.removeEventListener(eventName, eventListener);
    };
  }, [eventName, element]);
};

export default useEventListener;

Here's how you can use the useEventListener hook in a React component:

import { useRef } from 'react';
import useEventListener from './useEventListener';

const ExampleComponent = () => {
  const elementRef = useRef<HTMLDivElement>(null);

  const handleClick = () => {
    console.log('Element clicked!');
  };

  useEventListener('click', handleClick, elementRef);

  return (
    <div ref={elementRef}>
      <p>Click me!</p>
    </div>
  );
};

export default ExampleComponent;

In this example, the useEventListener hook takes three arguments: the name of the event to listen for, a function to handle the event, and an optional RefObject that refers to the element on which to listen for the event (defaulting to document). The ExampleComponent uses this hook to listen for clicks on a <div> element and log a message when clicked.

Last updated