Handling events with React elements is very similar to handling events on DOM elements. There are some syntactic differences:
For example, the HTML:
<button onclick="activateLasers()">Activate Lasers</button>
is slightly different in React:
<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 link behavior of opening a new page, you can write:
<a href="#" onclick="console.log('the link was clicked'); return false">
click me
</a>
In React, this could instead be:
function ActionLink() {
function handleClick(e) {
console.log('the link was clicked');
e.preventDefault();
};
return <a href="#" onClick={handleClick}>click me</a>;
}
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. See the SyntheticEvent
reference guide to learn more.
When using React you should generally not 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.
When you define a component using an ES6 class, a common pattern is for an event handler to be a method on the class. For example, this Toggle
component renders a button that lets the user toggle between "ON" and "OFF" states:
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {
toggleOn: true,
};
// This binding is necessary to make `this` work in the callback
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState({toggleOn: !this.state.toggleOn});
}
render() {
return (
<button onClick={this.handleClick}>
{this.state.toggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
You have to be careful about the meaning of this
in JSX callbacks. In JavaScript, class methods are not bound by default. If you forget to bind this.toggle
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. 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 you can get around this. If you are using the experimental property initializer syntax, you can use property initializers to correctly bind callbacks:
class LoggingButton extends React.Component {
// This syntax ensures `this` is bound within handleClick
handleClick = () => {
console.log('this is:', this);
}
render() {
return <button onClick={this.handleClick} />;
}
}
This syntax is enabled by default in Create React App.
If you aren't using property initializer syntax, you can use an arrow function 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()} />;
}
}
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 to avoid this sort of performance problem.