React onClick event vs JS addEventListener

So adding an onClick event is easy.


class App extends React.Component {

  handleClick = () => console.log('Hi there');

  render() {
    return <button onClick={this.handleClick}>Say something</button>;
  }
}

All you need to do is define your event handler function, and attach it to your HTML element inline.

But what about plain ol’ JavaScript event listeners?


document.addEventListener();

Would it be easier, and more beneficial to use that instead of React’s onClick inline?

Maybe. Let’s start with the basics.

What is a JS Event Listener

According to Mozilla JS docs, an event listener is a function that gets called when a specific event occurs.

Simple example, when a button gets clicked, something happens.

How to add a JS Event Listener


<button id="greetBttn">Click me!</button>

<script>
  // Find element
  const buttonEl = document.getElementById('greetBttn');

  // Add event listener
  buttonEl.addEventListener('click', () => alert("Hi user!"));
</script>

Okay, this isn’t too bad. Let’s do a code breakdown.

First I create a variable named buttonEl, and it’s value is an the button node element.

Then I’m adding an event listener by using, buttonEl.addEventListener().


addEventListener(type, handlerFunc);

The addEventListener function requires 2 arguments. What type of event you’re looking for, and the function to trigger after the event has happened.

You can look for all the event references here.

But like any good samaritan, you must clean up after yourself. You should always remove an event listener when you’re done using it.

How to remove a JS Event Listener


buttonEl.removeEventListener('click', () => alert("Hi user!"));

Just like that! You must use removeEventListener to clean up.

So far, from the look of using inline onClick and plain JS event listener, I think I’d rather go with onClick.

But there are pros to using addEventListener:

  • Works in every browser
  • You don’t need React to do this
  • You don’t need to import nothing to do this

What is React onClick

onclick is an inline event. Notice how I didn’t just call it React onclick.

That’s because this has been thing way before React.


<button onclick="alert('Hi there!');">Greet me!</button>

That code actually works, and events used to be written like that.

React has adopted this same style of attaching events inline, but have added there own touch to it.

All of React events are what they call Synthetic Events.

Quick overview of what is Synthetic Events

Synthetic Events is a React instance that all regular events go through in React.

React has created this to keep consistency for all browsers, and to increase performance since SyntheticEvent is pooled.

Does React remove event listener?

Yes.

Let’s take a look at plain JS event listener in a React component.


class App extends React.Component {

  handleClick = () => {
    alert("Hi there");
  };

  componentDidMount() {
    document.getElementById('foo')
      .addEventListener('click', this.handleClick)
  }

  componentWillUnmount() {
    document.getElementById('foo')
      .removeEventListener('click', this.handleClick)
  }

  render() {
    return <button id="foo">Say something</button>;
  }
}

Since I’ve added my own custom event listener, I have to clean it up myself.

But if we do it the React way, they handle that for us!


class App extends React.Component {

  handleClick = () => {
    alert("Hi there");
  };

  render() {
    return <button onClick={this.handleClick}>Say something</button>;
  }
}

And it’s less code. Double win for us!

Which to pick 99% of the time?

Just use React onClick Synthetic Event.

When to use addEventListener

There are a few a use cases where you need to implement your own event listener.

This is usually because of a specific UI behavior that you or the designers want to achieve.

An example is, when you have a modal popup, and you want to make it disappear when you click anywhere else but the dialog itself.

How to do that?

Here’s the code answer. For the sake of simplicity, I’m going to use inline styling as well.


const styles = {
  dialog: {
    position: "fixed",
    top: 0,
    left: 0,
    bottom: 0,
    right: 0,
    margin: "auto",
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    width: 200,
    height: 200,
    backgroundColor: "#fff",
    boxShadow: "0 2px 5px 0 rgba(0,0,0,0.25)"
  }
};

class App extends React.Component {
  state = {
    showDialog: true
  };

  handleDocumentClick = e => {
    // return element object or null
    const isClosest = e.target.closest(`[id=foo]`);

    if (this.state.showDialog && !isClosest) {
      this.setState({ showDialog: false });
    }
  };

  componentDidMount() {
    document.addEventListener("click", this.handleDocumentClick);
  }

  componentWillUnmount() {
    document.removeEventListener('click', this.handleDocumentClick);
  }

  render() {
    if (!this.state.showDialog) {
      return null;
    }

    return (
      <div id="foo" style={styles.dialog}>
        Click outside this box
      </div>
    );
  }
}

Conclusion

Neither approach is bad but, my recommendation is to use React’s onClick Synthetic Event because:

  • It handles event pooling
  • It handles removing event listeners for you
  • It’s better optimize
  • It’s more efficient on DOM resolution, and event delegation

This is not to say, never use custom event listeners. Use it only when you can’t do things with regular React events.

I like to tweet about React and post helpful code snippets. Follow me there if you would like some too!