Unbound event handler receiver¶
ID: js/unbound-event-handler-receiver
Kind: problem
Security severity:
Severity: error
Precision: high
Tags:
- correctness
Query suites:
- javascript-security-and-quality.qls
Click to see the query in the CodeQL repository
Event handler callbacks are usually invoked as functions, not as methods. This means that the this
expressions of such callbacks evaluate to undefined
or the global object. Using an ES6 class method as a callback therefore means that the this
expressions of the method do not refer to the class instance.
Recommendation¶
Ensure that the receiver object of event handler methods that use this
expressions is not undefined
. For instance, you can use bind
or explicitly invoke the method as a method call.
Example¶
The following example, for the React framework, registers the handleClick
method as an event handler for the click
event:
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<button onClick={this.handleClick}> // BAD `this` is now undefined in `handleClick`
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
This is problematic since this invokes handleClick
as a function call instead of a method call, meaning that this
is undefined
inside handleClick
.
Instead, bind the receiver of handleClick
in the constructor:
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}> // GOOD, the constructor binds `handleClick`
{this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
References¶
React Quick Start: Handling Events.