[React MAIN CONCEPTS] 6. Handling Events

Handling Events

Handling events with React elements is very similar to handling events on DOM elements. There are some syntax differences:

  • React events are named using camelCase, rather than lowercase.

  • With JSX you pass a function as the event handler, rather than a string.

For example, the HTML:

1
2
3
<button onclick="activateLasers()">
Activate Lasers
</button>

is slightly different in React:

1
2
3
<button onClick={activateLasers}>
Activate Lasers
</button>
  • Another difference is that you cannot return false to prevent default behavior in React. You must call preventDefault explicitly. For example, with plain HTML, to prevent the default form behavior of submitting, you can write:
1
2
3
<form onsubmit="console.log('You clicked submit.'); return false">
<button type="submit">Submit</button>
</form>

In React, this could instead be:

1
2
3
4
5
6
7
8
9
10
11
12
function Form() {
function handleSubmit(e) {
e.preventDefault();
console.log('You clicked submit.');
}

return (
<form onSubmit={handleSubmit}>
<button type="submit">Submit</button>
</form>
);
}

Synthetic Event

Here, e is a synthetic event. React defines these synthetic events according to the W3C spec, so you don’t need to worry about cross-browser compatibility. React events do not work exactly the same as native events. See the SyntheticEvent - https://reactjs.org/docs/events.html reference guide to learn more.

When using React, you generally don’t need to call addEventListener to add listeners to a DOM element after it is created. Instead, just provide a listener when the element is initially rendered.

ES6 Class event handler

When you define a component using an ES6 - https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes class, a common pattern is for an event handler to be a method on the class.

Bind event handler

For example, this Toggle component renders a button that lets the user toggle between “ON” and “OFF” states:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};

// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}

handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}

render() {
return (
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}

ReactDOM.render(
<Toggle />,
document.getElementById('root')
);

Try it on CodePen - https://codepen.io/gaearon/pen/xEmzGg?editors=0010

You have to be careful about the meaning of this in JSX callbacks. In JavaScript, class methods are not bound - https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind by default. If you forget to bind this.handleClick and pass it to onClick, this will be undefined when the function is actually called.

This is not React-specific behavior; it is a part of how functions work in JavaScript Understanding JavaScript Bind () — Smashing Magazine - https://www.smashingmagazine.com/2014/01/understanding-javascript-function-prototype-bind/. Generally, if you refer to a method without () after it, such as onClick={this.handleClick}, you should bind that method.

Class fields syntax

If calling bind annoys you, there are two ways you can get around this. If you are using the experimental public class fields syntax, you can use class fields to correctly bind callbacks:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class LoggingButton extends React.Component {
// This syntax ensures `this` is bound within handleClick.
// Warning: this is *experimental* syntax.
handleClick = () => {
console.log('this is:', this);
}

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

This syntax is enabled by default in Create React App - https://github.com/facebookincubator/create-react-app.

If you aren’t using class fields syntax, you can use an arrow function - https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions in the callback:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}

render() {
// This syntax ensures `this` is bound within handleClick
return (
<button onClick={() => this.handleClick()}>
Click me
</button>
);
}
}

The problem with this syntax is that a different callback is created each time the LoggingButton renders. In most cases, this is fine. However, if this callback is passed as a prop to lower components, those components might do an extra re-rendering. We generally recommend binding in the constructor or using the class fields syntax, to avoid this sort of performance problem.


Passing Arguments to Event Handlers

Inside a loop, it is common to want to pass an extra parameter to an event handler. For example, if id is the row ID, either of the following would work:

1
2
3
4
5
6
7
8
// Arrow function
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>

// Bind function
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

// (Recommend) Class field syntax
<button onClick={this.deleteRow(id, e)}>Delete Row</button>

The above three lines are equivalent.

In three cases, the e argument representing the React event will be passed as a second argument after the ID. With an arrow function, we have to pass it explicitly, but with bind any further arguments are automatically forwarded.

References

[1] Handling Events – React - https://reactjs.org/docs/handling-events.html

[2] React – A JavaScript library for building user interfaces - https://reactjs.org/

[3] SyntheticEvent – React - https://reactjs.org/docs/events.html

[4] Classes - JavaScript | MDN - https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes

[5] Function.prototype.bind() - JavaScript | MDN - https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_objects/Function/bind

[6] Understanding JavaScript Bind () — Smashing Magazine - https://www.smashingmagazine.com/2014/01/understanding-javascript-function-prototype-bind/

[7] facebook/create-react-app: Set up a modern web app by running one command. - https://github.com/facebookincubator/create-react-app

[8] Arrow function expressions - JavaScript | MDN - https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions