Skip to content
react

How to fix - this.setState is not a function error in React

Jan 21, 2022Abhishek EH3 Min Read
How to fix - this.setState is not a function error in React

If you are new to react and are using class-based components, you might have seen the following errors in the browser:

TypeError: Cannot read properties of undefined (reading 'setState')

TypeError: this.setState is not a function

Have you wondered why this error occurs and how to fix it? In this article, we will discuss the same.

Replicating the error

Consider the following code:

App.js
1import React, { Component } from "react"
2import "./App.css"
3
4export default class App extends Component {
5 constructor(props) {
6 super(props)
7
8 this.state = { counter: 0 }
9 }
10
11 updateCounter() {
12 this.setState({ counter: this.state.counter + 1 })
13 }
14
15 render() {
16 return (
17 <div className="App">
18 <button onClick={this.updateCounter}>
19 Clicked {this.state.counter} Times
20 </button>
21 </div>
22 )
23 }
24}

When you quickly scan through the code, you will think the code would perfectly fine. However, if you run the code and click on the button, nothing would happen. If you check the browser console, you will see the following error:

error - Cannot read properties of undefined - reading setState

Cause of the error

The reason why this error occurs is because of a concept called Closures in JavaScript. That means the scope/context of the function updateCounter and the class App are not the same. This means App and updateCounter have a different this.

In our case, we are trying to access this of the App class from within the function, which has its own this, which is currently undefined. Hence the error Cannot read properties of undefined.

How to fix the error

There are 2 ways to fix this issue:

Using ES6 arrow functions

The easiest way to fix the issue is to convert updateCounter function to an arrow function as shown below:

App.js
1import React, { Component } from "react"
2import "./App.css"
3
4export default class App extends Component {
5 constructor(props) {
6 super(props)
7
8 this.state = { counter: 0 }
9 }
10
11 updateCounter = () => {
12 this.setState({ counter: this.state.counter + 1 })
13 }
14
15 render() {
16 return (
17 <div className="App">
18 <button onClick={this.updateCounter}>
19 Clicked {this.state.counter} Times
20 </button>
21 </div>
22 )
23 }
24}

If you test the app now, you will see the counter getting updated when the button is clicked.

You may ask how changing the function to an arrow function fixed the issue out of the blue? What happened to this which was undefined? Well, arrow functions do not have this of their own! They refer to this of the lexical environment/context inside which they are declared.

In our case, the function updateCounter is defined inside the class App, and it refers to the this of the class, hence, this.setState works!

Binding this to the function

What if you do not want to use the arrow function (Well I do not see a reason why you should not!) and fix the code by using a traditional function? Well, there is a way!

We can bind this of App class to this of updateCounter function.

App.jsx
1import React, { Component } from "react"
2import "./App.css"
3
4export default class App extends Component {
5 constructor(props) {
6 super(props)
7
8 this.state = { counter: 0 }
9
10 this.updateCounter = this.updateCounter.bind(this)
11 }
12
13 updateCounter() {
14 this.setState({ counter: this.state.counter + 1 })
15 }
16
17 render() {
18 return (
19 <div className="App">
20 <button onClick={this.updateCounter}>
21 Clicked {this.state.counter} Times
22 </button>
23 </div>
24 )
25 }
26}

The syntax might look weird, but all it does is bind this of the class to that of the function.

These kinds of issues will not occur while using functional components with hooks since it does not make use of this.

Do follow me on twitter where I post developer insights more often!

© 2024 CodingDeft.Com