the event handler method loses its implicitly bound context. When the event occurs and the handler is invoked, the this value falls back to default binding and is set to undefined , as class declarations and prototype methods run in strict mode. In the beginning of this article, we saw this in our React component called Foo . If we did not bind the event handler with this , its value inside the event handler was set as undefined. When we bind the this of the event handler to the component instance in the constructor, we can pass it as a callback without worrying about it losing its context.
class Foo {
constructor(name) {
this.name = name
this.display = this.display.bind(this);
}
...
}
While working on React, you must have come across controlled components and event handlers. We need to bind these methods to the component instance using .bind()
in our custom component’s constructor.
class Foo extends React.Component{
constructor( props ){
super( props );
this.handleClick = this.handleClick.bind(this);
}
handleClick(event){
// your event handling logic
}
render(){
return (
<button type="button"
onClick={this.handleClick}>
Click Me
</button>
);
}
}
ReactDOM.render(
<Foo />,
document.getElementById("app")
);
Well, laying blame sounds a bit harsh. This is not something we need to do because of the way React works or because of JSX. This is because of the way the this
binding works in JavaScript.
Let’s see what happens if we do not bind the event handler method with its component instance:
class Foo extends React.Component{
constructor( props ){
super( props );
}
handleClick(event){
console.log(this); // 'this' is undefined
}
render(){
return (
<button type="button" onClick={this.handleClick}>
Click Me
</button>
);
}
}
ReactDOM.render(
<Foo />,
document.getElementById("app")
);
This is a plain function call. The value of this
inside the display()
method in this case is the window — or the global — object in non-strict mode. In strict mode, the this
value is undefined
.
var obj = {
name: 'Saurabh',
display: function() {
console.log(this.name); // 'this' points to obj
}
};
obj.display(); // Saurabh
When we call a function in this manner — preceded by a context object — the this
value inside display()
is set to obj
.
But when we assign this function reference to some other variable and invoke the function using this new function reference, we get a different value of this
inside display()
.
var name = "uh oh! global";
var outerDisplay = obj.display;
outerDisplay(); // uh oh! global
.bind(this) is used to bind the this context to your components function. However, it returns a new function reference each render cycle! If you don't want to bind on each usage of the function (like in a click handler) you can pre-bind the function. If you want to avoid creating a new function reference but still need to pass a parameter, its best to abstract that to a child component. You can read more about that here An arrow function does not have its own this, the this value of the enclosing execution context is used and hence the above function gets the correct context.
a. in your constructor do the binding. aka
class SomeClass extends Component {
constructor() {
super();
this.someEventHandler = this.someEventHandler.bind(this);
}
someEventHandler(event) {}
....
}
b. make your custom functions on the class fat arrow functions. aka
class SomeClass extends Component {
someEventHandler = (event) => {}
....
}
few common ways to do this
a. you can wrap your components handler function with an inline lambda (fat arrow) function.
onChange = {
(event) => this.someEventHandler(event)
}
// 1
return <input onChange={this.someEventHandler.bind(this)}>
This is just doing a runtime event handler bind to your class.
// 2
return <input onChange={(event) => this.someEventHandler(event) }>
In ReactJS, we need to bind events so that the this keyword would not return an “undefined“. In this article, we are going to see the different ways in which we can bind event handlers in ReactJS. Output: Now if we run the application and click on the button, we get an error. This is because this returns an “undefined”. This is why we need to bind our events. Let’s write an event that changes the state of message from ‘Welcome‘ to ‘Farewell‘ whenever the button is clicked. So let’s define an onClick method to the button and write an event handler clickHandler().
First, let’s make a new class component and name it Binding.js. Make a simple class component with a simple message state and render a simple button as shown below. Don’t forget to import this component in App.js file.
Binding.js:
import React, { Component } from 'react';
class EventBind extends Component {
constructor(props) {
super(props)
this.state = {
message: 'Welcome'
}
}
render() {
return (
<div>
<h3>{this.state.message}</h3>
<button>Click</button>
</div>
)
}
}
export default EventBind;
import React, { Component } from 'react';
class EventBind extends Component {
constructor(props) {
super(props)
this.state = {
message: 'Welcome'
}
}
clickHandler() {
this.setState({
message:'Farewell'
})
}
render() {
return (
<div>
<h3>{this.state.message}</h3>
<button onClick={this.clickHandler}>Click</button>
</div>
)
}
}
export default EventBind;
import React, { Component } from 'react';
class EventBind extends Component {
constructor(props) {
super(props)
this.state = {
message: 'Welcome'
}
}
clickHandler() {
this.setState({
message:'Farewell'
})
}
render() {
return (
<div>
<h3>{this.state.message}</h3>
<button onClick={this.clickHandler.bind(this)}>
Click</button>
</div>
)
}
}
export default EventBind;
this.clickHandler = this.clickHandler.bind(this)
import React, { Component } from 'react';
class EventBind extends Component {
constructor(props) {
super(props)
this.state = {
message: 'Welcome'
}
}
clickHandler = () => {
this.setState({
message:'Farewell'
})
}
render() {
return (
<div>
<h3>{this.state.message}</h3>
<button onClick={this.clickHandler}>
Click
</button>
</div>
)
}
}
export default EventBind;
With JSX you pass a function as the event handler, rather than a string. React events are named using camelCase, rather than lowercase. In both cases, the e argument representing the React event will be passed as a second argument after the ID. With an arrow function, we have to pass it explicitly, but with bind any further arguments are automatically forwarded. 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="activateLasers()">
Activate Lasers
</button>
<button onClick={activateLasers}> Activate Lasers
</button>
<form onsubmit="console.log('You clicked submit.'); return false">
<button type="submit">Submit</button>
</form>
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}> {this.state.isToggleOn ? 'ON' : 'OFF'}
</button>
);
}
}
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}>
Click me
</button>
);
}
}
If you need to have access to the parent component in the handler, you also need to bind the function to the component instance (see below). Using an arrow function in render creates a new function each time the component renders, which may break optimizations based on strict identity comparison. Using Function.prototype.bind in render creates a new function each time the component renders, which may have performance implications (see below). You can use an arrow function to wrap around an event handler and pass parameters:
<button onClick={this.handleClick}>
class Foo extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('Click happened');
}
render() {
return <button onClick={this.handleClick}>Click Me</button>;
}
}
class Foo extends Component {
handleClick = () => {
console.log('Click happened');
};
render() {
return <button onClick={this.handleClick}>Click Me</button>;
}
}
class Foo extends Component {
handleClick() {
console.log('Click happened');
}
render() {
return <button onClick={() => this.handleClick()}>Click Me</button>;
}
}
obj.method();
If you have used JavaScript before then you are familiar with the ‘this’ keyword and how it works. If you don’t bind the ‘this’ keyword with the event handler then it will point to undefined in the callback. This is JavaScript-specific behavior and is not related to React. There are five different ways to bind ‘this’ keyword to the event handler, listed below. If you want to do the same thing with a functional component then you can refer to the following example. Make the event handler function with an arrow function. In this case, we don’t need to bind the ‘this’ keyword.
Example:
In HTML:
1
2
3
<button onclick="clickHandler()">
Clicked
</button>
In React js
1
2
3
<button onClick={clickHandler}>
Clicked
</button>
Also, like in HTML, we cannot return false to prevent default behavior; we need to use preventDefault to prevent the default behavior.
In HTML
1
2
3
<form onsubmit="console.log('clicked'); return false">
<button type="submit">Submit</button>
</form>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function EventBind() {
const [steps, setSteps] = useState(0);
/*Note the fact that we are using the arrow function here.
We will discuss about it in binding section soon*/
const clickHandler = () => {
setSteps(steps + 1);
};
return (
<>
<div>{steps}</div> <
button onClick = {
clickHandler
} > Click < /button> <
/>
);
}
We can bind the ‘this’ keyword to the event handler in the constructor when using the class component. This is the most common way to handle this problem.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class EventBind extends React.Component {
constructor(props) {
super(props)
this.state = {
message: 'Event Bind'
}
this.clickHandler = this.clickHandler.bind(this)
}
clickHandler() {
this.setState({
message: 'change state'
})
}
render() {
return (
<>
<div>{this.state.message}</div> <
button onClick = {
this.clickHandler
} > Click < /button> <
/>
)
}
}