DEV Community

tieje
tieje

Posted on

2 2

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!

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)

Cloudinary image

Zoom pan, gen fill, restore, overlay, upscale, crop, resize...

Chain advanced transformations through a set of image and video APIs while optimizing assets by 90%.

Explore

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay