How to Use setInterval Callback with Context in React
Apr 25, 2020 • 6 Minute Read
Introduction
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.
Understanding the this Reference
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.
function bar() {
this.x = 43;
}
bar();
console.log(x); // will log 43
Look at another example below.
function bar() {
this.x = 43;
}
const barObject = {
barFn: bar
};
barObject.barFn();
console.log(x); // will log undefined
console.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.
The bind() Function
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.
class App extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
// ...
}
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 .
const User = {
lastName: "Wayne",
firstName: "Victor"
};
function logName() {
console.log(this.lastName, this.firstName);
}
logName(); // 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.
const User = {
lastName: "Wayne",
firstName: "Victor"
};
function logName() {
console.log(this.lastName, this.firstName);
}
const userBoundLogName = logName.bind(User);
userBoundLogName(); // 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.
const Victor = {
lastName: "Wayne",
firstName: "Victor"
};
const John = {
lastName: "Doe",
firstName: "John"
};
function logName() {
console.log(this.lastName, this.firstName);
}
const logVictor = logName.bind(Victor);
const logJohn = logName.bind(John);
logVictor(); // Wayne Victor
logJohn(); // Doe John
This way, you don't have to pass arguments explicitly to the logName() function; instead, you can bind objects as you scale.
Using the setInterval() Method
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.
import React, { Component } from "react";
class App extends Component {
state = { counter: 0 };
incrementCounter() {
const { counter } = this.state;
this.setState({ counter: counter + 1 });
}
componentDidMount() {
const thisBoundedIncrementer = this.incrementCounter.bind(this);
setInterval(thisBoundedIncrementer, 1000);
}
render() {
return (
<div className="App">
<h1>Counter</h1>
<h2>{this.state.counter}</h2>
</div>
);
}
}
export 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.
import React, { Component } from "react";
class App extends Component {
state = { counter: 0 };
incrementCounter = () => {
const { counter } = this.state;
this.setState({ counter: counter + 1 });
};
componentDidMount() {
setInterval(incrementCounter, 1000);
}
render() {
return (
<div className="App">
<h1>Counter</h1>
<h2>{this.state.counter}</h2>
</div>
);
}
}
export 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.
Conclusion
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.