In this guide, you'll dive into learning about the very familiar this
keyword. You may have worked with this
a zillion times, but still not know how it works and how it affects your React components. You may also have assumed that the this
keyword has to do a lot with React. But actually, it's just the way JavaScript binds the this
context.
The this
reference in JavaScript is identical to the this
reference in other class-based languages like Java or C#. It points to which object is calling the function. In JavaScript, however, the this
reference can be bound to different objects depending on where the function is being called. The this
reference is also called as context.
The context or the this
reference is by default bound to the global object when the function is called from the global scope.
1function bar() {
2 this.x = 43;
3}
4
5bar();
6console.log(x); // will log 43
Look at another example below.
1function bar() {
2 this.x = 43;
3}
4
5const barObject = {
6 barFn: bar
7};
8
9barObject.barFn();
10
11console.log(x); // will log undefined
12console.log(barObject.x); // will log 43
In this case, the bar()
function is being called from the context of barObject
object, so the this
reference is bound to barObject
. If you try to log the variable x
, it will return undefined
as the function bar()
is not called from the global scope.
You have probably come across the bind()
function, which is used for binding the this
reference of the class component to the event handlers. It looks something like this.
1class App extends React.Component {
2 constructor(props) {
3 super(props);
4 this.handleClick = this.handleClick.bind(this);
5 }
6 // ...
7}
The bind()
function can be confusing, especially when you are just beginning with JavaScript or React. This section will explain what bind()
does and how to use it.
Have a look at the example below .
1const User = {
2 lastName: "Wayne",
3 firstName: "Victor"
4};
5
6function logName() {
7 console.log(this.lastName, this.firstName);
8}
9
10logName(); // undefined, undefined
If you run the above code, the logName()
function will console log undefined
. That's because the value of the this
reference is currently bound to the global object, and since there's no lastName
or firstName
variables defined in the global context, it returns undefined
.
Now, bind the User
object to the logName()
function and watch the magic.
1const User = {
2 lastName: "Wayne",
3 firstName: "Victor"
4};
5
6function logName() {
7 console.log(this.lastName, this.firstName);
8}
9
10const userBoundLogName = logName.bind(User);
11
12userBoundLogName(); // Wayne Victor
Here, the bind()
function binds the User
object to the logName()
function and returns a bounded function. The bounded function has the this
reference pointing to the User
object. You can create many references to the logName()
function by binding to new objects, as shown below.
1const Victor = {
2 lastName: "Wayne",
3 firstName: "Victor"
4};
5
6const John = {
7 lastName: "Doe",
8 firstName: "John"
9};
10
11function logName() {
12 console.log(this.lastName, this.firstName);
13}
14
15const logVictor = logName.bind(Victor);
16const logJohn = logName.bind(John);
17
18logVictor(); // Wayne Victor
19logJohn(); // Doe John
This way, you don't have to pass arguments explicitly to the logName()
function; instead, you can bind objects as you scale.
Now that you have a good sense of the this
keyword and the bind()
function, take a look at how it can be used with setInterval()
inside a component.
By default, the this
reference will always be set to the global object. When working with class methods, you'll explicitly need to bind the this
reference in order for the setInterval()
function to reference the current class instance.
Take a look at a simple counterexample below.
1import React, { Component } from "react";
2
3class App extends Component {
4 state = { counter: 0 };
5
6 incrementCounter() {
7 const { counter } = this.state;
8 this.setState({ counter: counter + 1 });
9 }
10
11 componentDidMount() {
12 const thisBoundedIncrementer = this.incrementCounter.bind(this);
13 setInterval(thisBoundedIncrementer, 1000);
14 }
15
16 render() {
17 return (
18 <div className="App">
19 <h1>Counter</h1>
20 <h2>{this.state.counter}</h2>
21 </div>
22 );
23 }
24}
25
26export default App;
In the above code snippet, before passing the callback to the setInterval()
function, you need to bind it to the current instance of the component. If you don't, the setInterval()
callback won't have the context to this.state
.
Hold on. There's an exception to the this
reference in which you don't have to bind it to a function: when you use an arrow function.
In the above code, refactor the incrementCounter()
function declaration to an arrow function.
1import React, { Component } from "react";
2
3class App extends Component {
4 state = { counter: 0 };
5
6 incrementCounter = () => {
7 const { counter } = this.state;
8 this.setState({ counter: counter + 1 });
9 };
10
11 componentDidMount() {
12 setInterval(incrementCounter, 1000);
13 }
14
15 render() {
16 return (
17 <div className="App">
18 <h1>Counter</h1>
19 <h2>{this.state.counter}</h2>
20 </div>
21 );
22 }
23}
24
25export default App;
In case of an arrow function, you don't have to bind the this
reference. That's because an arrow function does not have a this
reference in its context, and by default the this
reference points to the class instance.
Using the this
keyword and binding with the bind()
function are fundamental concepts in JavaScript. You can bind any objects to a function and pass the this
context. By default, this
refers to the global object. Other than the bind()
function, you can also use the call()
or apply()
method to change the value of the this
reference.