I put 18 event handlers on a React element
Tags: react, javascript, webdev
Published: 2019-01-28 12:29:39

---
title: I put 18 event handlers on a React element
published: true
description: Let's add a lot of synthetic events to a React element and see what happens.
tags: react, javascript, webdev
series: 💥 React Treats
---
![Synthetic Event Party!](https://thepracticaldev.s3.amazonaws.com/i/z84rzkapzmzgpytkcg9j.jpg)

If you've ever built a React app you've probably encountered a `SyntheticEvent`. Good chance it was `onChange`, but maybe you've been a little more adventurous and it was `onKeyDown`.

In a technical sense a `syntheticEvent` is a wrapper that's part of React. It takes the place of the native event handlers you might know from plain Javascript.
 
Let's say we have a `<button />` and we want something to happen when a user clicks on it. In plain Javascript we would add `onclick` to the element. That doesn't work in React. Instead the library provides its own handlers which mimic the functionality and make them work equally across browsers. They look a lot like the native handlers though. For example: `onclick` in React is `onClick`.

[You can always read more about them in the docs.](https://reactjs.org/docs/events.html)

## Fire 'm up!
Now we could go through the entire list of events and explain them one-by-one, but to really get a sense of what's going on when you add one of these handlers to an element, let's just hook them up.

I've picked 18 of them. There are more, but these are the most common ones. We're going to add them to an `<input />` element.

Since the objective is to get a feel for them, let's try to answer two questions:

- when do they fire?
- how often do they fire?

The first question we're going to answer by giving a visual cue upon firing, and the second question can be answered by keeping a log. Let's start building.

A synthetic event handler accepts a function. So we're going to add a function to all 18 handlers.

```javascript
<input
  onCopy={() => this.addEvent("onCopy")}
  onCut={() => this.addEvent("onCut")}
  onPaste={() => this.addEvent("onPaste")}
  onKeyDown={() => this.addEvent("onKeyDown")}
  onKeyPress={() => this.addEvent("onKeyPress")}
  onKeyUp={() => this.addEvent("onKeyUp")}
  onFocus={() => this.addEvent("onFocus")}
  onBlur={() => this.addEvent("onBlur")}
  onChange={() => this.addEvent("onChange")}
  onClick={() => this.addEvent("onClick")}
  onDoubleClick={() => this.addEvent("onDoubleClick")}
  onMouseDown={() => this.addEvent("onMouseDown")}
  onMouseEnter={() => this.addEvent("onMouseEnter")}
  onMouseLeave={() => this.addEvent("onMouseLeave")}
  onMouseMove={() => this.addEvent("onMouseMove")}
  onMouseOver={() => this.addEvent("onMouseOver")}
  onMouseUp={() => this.addEvent("onMouseUp")}
  onSelect={() => this.addEvent("onSelect")}
/>
```
As you might notice there is an anonymous in-line function that actually calls the real `this.addEvent` function. We have to do this because we want to pass an argument into the function; the name of the event.

The next step is to write the actual `addEvent` function. Before we write it, let's remember what we need to do. We need a visual cue upon each triggering of an event and we need to keep a count of each event being triggered. Let's actually start with the latter to see how many events fire. That could affect our idea of what we want to happen with regards to the visual cues.

### Keeping a log
Our log of counts is a piece of data that changes upon user input. That means we're going to use `state`. The specific data structure we'll use is an `array` with `objects` inside of them. Each `object` will represent each type of synthetic event, and will have both a `name` property and an `amount` property. It would look like this:

```javascript
[{ name: "onChange", amount: 1 }, { name: "onClick", amount: 5 }]
```
Since we're starting out with an empty array without any counts, the first thing we need to do on each firing of the function is to check whether we need to add a new event to the array. If, however, we find that the event was already added to the array, we only need to increase the count.

```javascript
addEvent = event => {
  const existingEvent = this.state.counts.filter(c => c.name === event)[0];
  const amount = existingEvent ? existingEvent.amount + 1 : 1;
  const count = this.state.counts.map(c => c.name).includes(event)
    ? Object.assign({}, existingEvent, { amount })
    : { name: event, amount };
};
```
So the `existingEvent` will either contain data or remain empty. With that info we can determine the `amount` property. And finally we either have to update the existing object, or prepare a new one.

With that in place we need to update the `state`. Since our `counts` data is an array, and we now have an object, we need to either find and replace an existing object, or just tag the new object onto the array.

```javascript
  const counts = produce(this.state.counts, draftState => {
    if (existingEvent) {
      const index = this.state.counts.findIndex(c => c.name === event);
      draftState[index] = count;
    } else {
      draftState.push(count);
    }
  });

  this.setState({counts})
```

Now you might see an unfamiliar function here: `produce`. This is not a function I wrote myself, but one I exported from a library called `immer`. I highly recommend you [check out that library](https://github.com/mweststrate/immer) if you are in the business of mutating data, but love your immutable data structures. `immer` allows you to work with your data as if you were directly mutating it, but via a 'draft state' keeps both your old and new state separated.

With that in place we now have a new version of our `counts` state we can put in the place of the current version of our `counts` state. The only thing that's left to do is to render this data onto the page, so we can actually see the counts.

In our `render()` function we can map our `counts` array into a list.

```javascript
const counts = this.state.counts.map(c => {
  return (
    <li key={c.name}>
       {c.name} <strong>{c.amount}</strong>
    </li>
  );
});
```
And in our `return` we can add the items to our `<ul />`.

```javascript
 <ul>{counts}</ul>
```
Now we should be able to see our synthetic events pop up with their respective counts. Try and see if you can fire up all 18 of them.

You might notice that events like `onMouseMove` fire up way more than others. This informs us that for our visual cues we have to be a bit mindful about that. And speaking about visual cues, let's set them up.

### Party time
My idea is to render the name of the event on a random position on the screen on each trigger, and make it disappear again after a second or two. To make it a bit more clear which events fires, we will add specific styling for each event. Let's do that part first.

```javascript
function getStyle(event) {
  let style;
  switch (event) {
    case "onCopy":
      style = {
        fontFamily: "Times New Roman",
        fontSize: 50,
        color: "red"
      };
      break;
    case "onCut":
      style = {
        fontFamily: "Tahoma",
        fontSize: 40,
        color: "blue"
      };
      break;
    case "onPaste":
      style = {
        fontFamily: "Arial",
        fontSize: 45,
        color: "salmon"
      };
      break;
  }
  return style;
}
```
For reasons of brevity, these are not all 18 cases. You can find those in the [full code](https://codesandbox.io/s/qqpl1kjy8q), but you'll get the gist of it. Based on the event, we return a style object with a unique font size, font family and color.

The next part is to get the random position on the screen.

```javascript
function getRandomNumber(min, max) {
  return Math.random() * (max - min) + min;
}

function getPosition() {
  return {
    left: getRandomNumber(0, window.innerWidth - 120),
    top: getRandomNumber(0, window.innerHeight - 120)
  };
}
```
The `getPosition` function returns a style object with a random number between 0 and the width or height of the screen. I've deducted 120 pixels, so the events don't fall of the screen.

With these helpers in place, let's think about how to actually make the events show up on our screen. We have already implemented the `counts` so we have a bit of an idea how to do this. The difference is that this time we want to save each event as a separate object we can render on the screen, only to get rid of that object after 2 seconds of time. That means for each event we need to update the state twice.

Let's start with updating the state just once.

```javascript
  const id = shortId.generate();
  const position = getPosition();
  const style = getStyle(event);
  const events = [...this.state.events, { id, event, position, style }];
```
We first generate a unique `id` for each event using the `shortid` library. The reason for this, is that we need to be able to find the event again after it has been added to the state, so we can remove it.

Next we get our position and style object, which we'll need later to render the events on the screen. Finally, we create a new version of our `events` state.

If we update our state now and keep triggering events, we're going to get a huge array full of events, which will clog up the screen. So, we need to constantly clean up the array. How to do that?

An effective trick is to use `setTimeOut`, which is a little timer. After each update we wait 2 seconds, grab the `id` of the event we just added, and remove it again.

```javascript
 this.setState({ events }, () => {
   setTimeout(() => {
     const events = this.state.events.filter(e => e.id !== id);
     this.setState({ events });
   }, 2000);
 });
```
We start out with our regular `setState` in which we update the `events` array we just created. But then as a second argument we add a new anonymous function. By doing this in the second argument of `setState` we ensure the initial update of `events` has been applied.

Within that callback function, we set our timeout to 2 seconds, and create an updated version of our `events` with the now updated `state`. Since we are still in the same `addEvent` function, we know the `id` and we can easily filter it out. Then we set our state for the second time.

Now if we were to log `this.state.events` we should see it fill up and empty out. But it's more fun to see that on our screen. After all, we have a style object with random positions and unique formatting. So let's do a `.map` again in our component and see how it turned out.

```javascript
const events = this.state.events.map(event => {
  return (
    <div
      key={event.id}
      style={{
        position: "absolute",
        left: event.position.left,
        top: event.position.top,
        zIndex: -1,
        opacity: 0.5,
        ...event.style
        }}
     >
      {event.event}
     </div>
   );
});
```
As you can see we both add the position and styling of each `event` object to the element. We now only have to add the `events` variable to our `return`.

And with that we now have a nice synthetic event party on our screen. Aside from the visual fun we have just created, I hope you also get a feel for when each event triggers. Not each event will be super relevant in your day-to-day work, but sometimes it can be useful to know when `onMouseLeave` fires or just be aware that `onDoubleClick` exists.

See the full code in action [here](https://codesandbox.io/s/qqpl1kjy8q). Happy to chat on [Twitter](https://twitter.com/ma5ly)