Handling Events
# 05. Handling Events
# Differences from Traditional HTML Event Binding
Traditional HTML:
<button onclick="activateLasers()">
Activate Lasers
</button>
2
3
In React, it's slightly different:
// 1. Event name uses camelCase; 2. Pass a function wrapped in curly braces
<button onClick={activateLasers}>
Activate Lasers
</button>
2
3
4
- React events are named using camelCase, rather than lowercase.
- With JSX you pass a function as the event handler, rather than a string.
# Preventing Default Behavior (Synthetic Event Object)
In React, you prevent default behavior like this:
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked.');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
2
3
4
5
6
7
8
9
10
11
12
Here, e is a synthetic event. React defines these synthetic events according to the W3C spec (opens new window), 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 (opens new window) reference guide to learn more.
# Ways to Bind Events
When using React, you generally don't need to use addEventListener to add listeners to created DOM elements. Instead, just add a listener when the element is initially rendered.
When you define a component using an ES6 class (opens new window), a common pattern is to make an event handler a method on the class. For example, the Toggle component below renders a button that lets the user toggle between states:
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);
}
hadleClick(){
this.setState(state => ({
isToggleOn: !state.isToggleOn
}));
}
render(){
return(
// To use this.handleClick here, we need to bind handleClick to this in the constructor
<button onClick={this.handleClick}>
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
)
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
)
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
29
You have to be careful about the meaning of this in JSX callbacks. In JavaScript, class methods are not bound (opens new window) 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 (opens new window). Generally, if you refer to a method without () after it, such as onClick={this.handleClick}, you should bind that method.
If calling bind annoys you, there are two ways around it. If you are using the experimental public class fields syntax (opens new window), you can use class fields to correctly bind callbacks:
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>
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
Create React App (opens new window) enables this syntax by default.
If you aren't using class fields syntax, you can use an arrow function (opens new window) in the callback:
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>
);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
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 extra re-rendering. We generally recommend binding in the constructor or using class fields syntax to avoid this sort of performance problem.
# Event Naming Convention
In React, there is a naming convention: event-listening props are typically named on[Event], and event-handling methods are named handle[Event].
# 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:
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
2
The above two lines are equivalent, and use arrow functions (opens new window) and Function.prototype.bind (opens new window) respectively.
In both cases, the e argument representing the React event will be passed as a second argument. With an arrow function, we have to pass it explicitly, but with bind any further arguments are automatically forwarded.