Do you know what key did you press?

Do you know what key did you press?

How to handle key events in React JS

The inspiration was really simple. I was just driving the car and listening to the Syntax podcast. Wes Bos and Scott Tolinski were talking about keyboard event listeners. So I got an idea 💡.

What problem do I need to solve?

Sometimes when I am developing some feature into the application, there is a request to handle some actions by pressing key or key's combination on the keyboard. To handle the pressing key events, I do need to know what key was pressed. Each key has its own unique code. This code needs to be handled to recognize the key.

So how do I know how to handle key code? Where can I find this value?

On key press, there is an event send to the handler. This event stores information. For the keypress, it is a specific event called KeyboardEvent. KeyboardEvent objects describe a user interaction with the keyboard. It just indicates what interaction the user had with a key on the keyboard at a low level, providing no contextual meaning to that interaction.

There is a lot of pieces of information saved in the event. There are boolean values, strings, numbers, functions e.t.c. I do not need all of this data. I do need only a unique variable to be able to decide about the actions.

How do I know what is the correct value from the event?

When to trigger an action

Before we are able to get some values from the keys, we need to define, when we what to receive these data. We need to listen for some event. Do we want to trigger an action when we press the key, when we release the key, or when it is constantly pressed down?

There are three types of events we can listen to. Keydown, keypress, or keyup.

window.document.listen('keydown', callback); // create event listener
window.document.listen('keyup', callback); // destroy event listener

Get only useful data

One of the options is to learn about the events, find out which values are useful and what are the other values for.

Once we know what do we need, we can program a function to get only these values. Let's say, we want to get Event.keyCode and Event.code.

Event.code is the string value. The advantage of it is, when you press for example space key, you get "Space" string value. Event.keyCode returns number. There is also option to have Event.key, although when you press space, you get an empty value, so you are able to recognize the key.

function(e: KeyboardEvent) {
  return {
    keyCode: e.keyCode, // number
    code: e.code, // string
  };
}

Problem solution

I realized, that I am probably not the only one who is working with the keyboard events. So I was just asking myself, how can I solve the problem for the other developers as well?

Simple tool, that tells you, what did you press

First, I was thinking about a very simple web application that will listen to user's interactions with the keyboard. The output of the application is an interface where users can see something like "console" with the pressed key information.

So I developed whatdidipress.com. I created it in NextJS.

During the development, I also realized a couple of things. I tried to develop a pattern that could be helpful and probably any other programmers will appreciate it. So I made an NPM package.

NPM package

Create the package is one of the simplest ways how to share your code with others. the only thing was just to decide what part of code I want to do as open-source. The most useful one. Right?

For the web application, I created a container where I wrapped the whole application in. So the whole application is listening to the events and I have access to do some action across the whole application from any place. The container is returning an object with the reduced key event values.

// Container.js
const KeyListenerContainer = ({ children, onKeyDownCallback }) => {
  const [keyInfo, setKeyInfo] = useState({ code: null });

  useEffect(() => {
    addKeyListener();

    return () => {
      removeKeyListener();
    }
  }, []);

  const addKeyListener = () => {
    document.addEventListener('keydown', onKeyDown);
    document.addEventListener('keyup', onKeyDown);
  };

  const removeKeyListener = () => {
    document.removeEventListener('keydown', onKeyDown);
    document.removeEventListener('keyup', onKeyDown);
  };

  const onKeyDown = useCallback(e => {
    onKeyDownCallback(e);

    setKeyInfo({ code: e.code });
  }, []);

  return children({
    keyInfo,
  });
};

export default KeyListenerContainer;

Hook useKeyboardKey

Nowadays, it is very popular to use React hooks. I created one, so it is on developer preferences what he would like to use.

// hook.js
import { useEffect, useState } from 'react';

export const useKeyboardKey = (onKeyDownCallback) => {
  const [keyInfo, setKeyInfo] = useState({ code: null });

  useEffect(() => {
    addKeyListener();

    return () => {
      removeKeyListener();
    }
  }, []);

  const addKeyListener = () => {
    document.addEventListener('keydown', onKeyDown);
    document.addEventListener('keyup', onKeyDown);
  };

  const removeKeyListener = () => {
    document.removeEventListener('keydown', onKeyDown);
    document.removeEventListener('keyup', onKeyDown);
  };

  const onKeyDown = useCallback(e => {
    onKeyDownCallback(e);

    setKeyInfo({ code: e.code });
  }, []);

  return { keyInfo, addKeyListener, removeKeyListener }; 
};

And now we have a reusable solution, which we can use everywhere we want.

Conclusion

The question is - do we solve the problem from the beginning? My problem was that I wanted to minimalize and reduce the code I have to write when I am working with the events. Also, I wanted to reduce data to useful ones so I can easier decide what should my conditions contain.

First learned about KeyboardEvents, so I was able to write a code. Then I created an application that tells me, what did I press. It is useful so during the following development I do not need to debug and log the events -> time saved.

The most useful and most important part of it is, solution of my problem is reusable, shared with others, and open to collaboration. If there is anything missing, anyone can open the pull request and improve the package, which I will appreciate.

Resources