6. Event handling
Using React elements to handle events is very similar to handling events on DOM elements. But there are some grammatical differences:
-
React events are named using hump naming, not all lowercase names.
-
Using JSX, you pass a function as an event handler, not a string.
For example, HTML:
<button onclick="activeLasers()">
Active Lasers
</button>
There are slightly different in React:
<button onClick={activateLasers}>
Active Lasers
</button>
Another difference is that you cannot return false to prevent default behavior in React. You must explicitly call preventDefault. For example, with pure HTML, in order 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 can be changed to:
function ActiveLink() {
function handleClick(e) {
e.preventDefault();
console.log('The link was clicked');
}
return (
<a href='#' onClick={handleClick}>
click me
</a>
);
}
Here, e is a synthetic event. React defines these events according to the W3C specification, so you don't need to worry about browser compatibility.
When using React, adding DOM elements after creation usually does not require calling the addEventListener listener. Instead, event listeners are provided only when the element is initially rendered.
When you define components using ES6 classes, the common pattern is to use event handlers as common methods on classes. For example, this Toggle component presents a button that allows users to switch between ON and OFF states:
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
// This binding is necessary, otherwise this in the event will be the current instance
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')
);
You have to pay great attention to the meaning of event functions in JSX. In JavaScript, methods in classes do not bind this by default. If you forget to bind the this keyword in this.handleClick and pass it to the onClick event, an undefined error will be reported when the function is actually called.
This is not a specific behavior in React; it is part of the normal functionality of functions in JavaScript.
Generally speaking, if you refer to a method that is not followed by (), such as onClick
= {this.handleClick} binds the method.
If calling bind bothers you, there are two ways to solve this problem. You can use property initialization to set props to handle properly:
class LoggingButton extends React.Component {
// Binding this with a clipping function
handleClick = () => {
console.log('this is:', this);
}
render() {
return (
<button onClick={this.handleClick}>
Click me
</button>
);
}
}
By default, this grammar is enabled in Create React App.
If you do not use attribute initialization syntax, you can use arrow functions in callbacks:
class LoggingButton extends React.Component {
handleClick() {
console.log('this is:', this);
}
render() {
// Passing in a clipping function to an event can also bind this
return (
<button onClick={(e) => this.handleClick(e)}>
Click me
</button>
);
}
}
The problem with this grammar is that different callback functions are created each time LoggingButton is rendered. In most cases, this is a fine. However, if this callback is passed as a prop to lower components, these components may be rendered extra. We generally recommend binding in constructors to avoid this performance problem.