React Event Handling
Event handling in React elements is similar to that in DOM elements. However, there are some syntactic differences:
React event binding attributes are named using camelCase, rather than lowercase.
If you use JSX syntax, you need to pass a function as the event handler, rather than a string (as in DOM elements).
The typical HTML way is:
<button onclick="activateLasers()">
Activate Button
</button>
In React, it is written as:
<button onClick={activateLasers}>
Activate Button
</button>
Another difference in React is that you cannot prevent default behavior by returning false. You must explicitly use preventDefault
.
For example:
<a href="#" onclick="console.log('Link clicked'); return false">
Click me
</a>
In React, it is written as:
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('Link clicked');
}
return (
<a href="#" onClick={handleClick}>
Click me
</a>
);
}
In the example, e
is a synthetic event.
When using React, you usually do not need to use addEventListener
to add a listener to a created DOM element. You simply need to provide a listener when the element is initially rendered.
When you use ES6 class syntax to define a component, event handlers become methods of the class. For example, the following Toggle
component renders a button that allows the user to toggle a switch:
Example
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// Binding here is necessary so that `this` can be used 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('example')
);
You must be careful with this
in JSX callback functions. Class methods do not automatically bind this
. If you forget to bind this.handleClick
and pass it to onClick
, this
will be undefined
when the function is called.
This is not specific to React; it is part of how functions work in JavaScript. Generally, if you do not append ()
to a method, such as onClick={this.handleClick}
, you should bind this
to the method.
If binding bothers you, there are two ways to solve it. If you are using experimental property initializer syntax, you can use property initializers to correctly bind callback functions:
class LoggingButton extends React.Component {
// This syntax ensures that `this` is bound within handleClick
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
If you are not using property initializer syntax, you can use arrow functions in the callback:
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
return (
<button onClick={() => this.handleClick()}>
Click me
</button>
);
}
}
This syntax ensures that this
is bound within handleClick
.
return (
<button onClick={(e) => this.handleClick(e)}>
Click me
</button>
);
}
}
Using this syntax has a drawback: 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 such performance issues.
Passing Arguments to Event Handlers
It is common to pass extra parameters to event handlers. For example, if id
is the ID of the row you want to delete, either of the following approaches will work:
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
<button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>
In both examples, the event object e
will be passed as the second argument. With the arrow function, we have to pass it explicitly, but with bind
any further arguments are automatically forwarded.
It's important to note that when using bind
, the event object e
should be placed after the parameters in the listener function defined in the class component, for example:
class Popper extends React.Component {
constructor() {
super();
this.state = { name: 'Hello world!' };
}
preventPop(name, e) { // The event object e should be placed last
e.preventDefault();
alert(name);
}
render() {
return (
<div>
<p>hello</p>
{/* Passing parameters via bind() method */}
<a href="https://reactjs.org" onClick={this.preventPop.bind(this, this.state.name)}>Click</a>
</div>
);
}
}