DEV Community

tieje
tieje

Posted on

How to Listen to Events in ReactJS

TL;DR

Contents

Introduction

  In my last article, I created a static hexagonal grid with pan and zoom functionality. That's cool, but what's not cool is needing to press the one of the follow buttons to switch between pointer mode and drag mode:

SVG app tools

  Instead of pressing these toolbar buttons, I'd like to use a keyboard shortcut to switch between the pointer mode and the drag mode. Copying Figma's button shortcuts, specifically, I would like to bind the v button and the h button keyboard keys to the pointer mode and drag mode, respectively. This functionality was achieved thanks to the use-event-listener hook. Fortunately, the React hook is simple enough to use as brief case study into how event listeners work.

How It Works

  The useEventListener hook is only one file with a brief amount of code:

/* eslint-disable max-params */
import { useRef, useEffect } from 'react';

const useEventListener = (
  eventName,
  handler,
  element = global,
  options = {}
) => {
  const savedHandler = useRef();
  const { capture, passive, once } = options;

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

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

    const eventListener = (event) => savedHandler.current(event);
    const opts = { capture, passive, once };
    element.addEventListener(eventName, eventListener, opts);
    return () => {
      element.removeEventListener(eventName, eventListener, opts);
    };
  }, [eventName, element, capture, passive, once]);
};

export default useEventListener;
Enter fullscreen mode Exit fullscreen mode

donavan's original code

  Let's break it down.

const useEventListener = (
  eventName,
  handler,
  element = global,
  options = {}
) => {
  const savedHandler = useRef();
  const { capture, passive, once } = options;
Enter fullscreen mode Exit fullscreen mode

  The useEventListener requires the event name and the handler function. In my case, the event I'm looking for is keypress and the function I made is handlePanZoomModeSwitch

useEventListener('keypress', handlePanZoomModeSwitch)
Enter fullscreen mode Exit fullscreen mode

  Because I'm simply checking if the keyboard v or h key is pressed, it's perfectly fine to use the default element global.

  There are three available options, despite four possible options. None of them fit my needs, however.

  useEffect(() => {
    savedHandler.current = handler;
  }, [handler]);
Enter fullscreen mode Exit fullscreen mode

savedhandler is assigned to the useRef() which is used to access DOM nodes and persist mutable values across re-renders. In our, case we don't want to forget whatever state is already attached to the current DOM in our window. Since the second useEffect() parameter is specified as [handler], whenever the handler function changes, the component will re-render itself. If the second useEffect() parameter was not specified, as in simply being [], then the component will only render the component once.

  The last useEffect hook looks lengthy, but it's not that complex. The isSupported if-statement just checks if the element exists and if we can add an event listener to that element.

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

    const eventListener = (event) => savedHandler.current(event);
    const opts = { capture, passive, once };
    element.addEventListener(eventName, eventListener, opts);
    return () => {
      element.removeEventListener(eventName, eventListener, opts);
    };
  }, [eventName, element, capture, passive, once]);
Enter fullscreen mode Exit fullscreen mode

  Next, the eventListener arrow function serves as the handler function for the addEventListener. The eventListener function simply passes whatever event occurs to the handler function that we specified.

Finally, the removeEventListener() is passed to prevent memory leaks, side-effects, and event collisions.

Conclusion

The useEventListener() hook makes it easy to bind keys for your web app needs. Have fun out there!

Top comments (0)