Skip to content

Event Handlers

Before we look at the nuts and bolts of working with state in React, we need to speak a little bit about events.

As the user interacts with the page, hundreds of events are fired off in response. The browser is like an invasive private investigator, tracking every little thing you do.

The following demo logs a bunch of different common events. Try interacting with this widget, and notice the stream of events that are subsequently logged:

When we're building dynamic web applications, these events become super important. We'll listen for these events, and use them to trigger state changes. When the user clicks the "X" button, we dismiss the prompt. When the user submits the form, we show a loading spinner.

In order to respond to an event, we need to listen for it. JavaScript provides a built-in way to do this, with the addEventListener method:

const button = document.querySelector('.btn');
function doSomething() {
// Stuff here
}
button.addEventListener('click', doSomething);

In this code, we're listening for a specific event (clicks) targeting a specific element (.btn). We have a function which is meant to handle this event, doSomething. When the user clicks this particular button, our handler function will be invoked, allowing us to do something in response.

The web platform offers another way to do this as well. We can embed our handler right in the HTML:

<button onclick="doSomething()">
Click me!
</button>

React piggybacks on this pattern, allowing us to pass an event handler right in the JSX:

function App() {
function doSomething() {
// Stuff here
}
return (
<button onClick={doSomething}>
Click me!
</button>
);
}

As with addEventListener, this code will perform the same duty: when the user clicks the button, the doSomething function will be called.

This is the recommended way to handle events in React. While we do sometimes have to use addEventListener for window-level events (covered in Module 3), we should try and use the “on X” props like onClick and onChange whenever possible.

There are a few good reasons why:

  1. Automatic cleanup. Whenever we add an event listener, we're also supposed to remove it when we're done, with removeEventListener. If we forget to do this, we'll introduce a . React automatically removes listeners for us when we use “on X” handler functions.
  2. Improved performance. By giving React control over the event listeners, it can optimize things for us, like batching multiple event listeners together to reduce memory consumption.
  3. No DOM interaction. React likes for us to stay within its abstraction. We generally try and avoid interacting with the DOM directly. In order to use addEventListener, we have to look up the element with querySelector. This is something we should avoid. The “happy path” in React involves letting React do the DOM manipulation for us.

Differences from HTML

When we look at the onChange prop, it looks very similar to the “in HTML” method of adding event handlers. There are some key differences though.

Camel Casing

As we saw in the last module, we need to write “camelCased” attribute names in JSX. Be careful to write onClick, with a capital “C”, instead of onclick. Same thing with onChange, onKeyDown, onTransitionEnd, etc.

Here's the good news: if you forget, you'll get a helpful console warning letting you know about your mistake:

Warning: Invalid event handler property onclick. Did you mean onClick?

This common mistake tends to be pretty easy to spot. Let's look at another common issue that, unfortunately, is harder to spot.

Passing a function reference

When working with event handlers in React, we need to pass a reference to the function. We don't call the function, like we do in HTML:

// ✅ We want to do this:
<button onClick={doSomething} />
// 🚫 Not this:
<button onClick={doSomething()} />

When we include the parentheses, we invoke the function right away, the moment the React application is rendered. We don't want to do that; we want to give React a reference to the function, so that React can call the function at a later time, when the user clicks on the button.

In case JSX is getting in the way, here's the same code written in compiled JavaScript:

// ✅ Correct.
// Will be called when the user clicks the button.
React.createElement(
'button',
{
onClick: doSomething,
}
);
// 🚫 Incorrect
// Will be called immediately, when the element is created.
React.createElement(
'button',
{
onClick: doSomething(),
}
);