At some point in your development career, you may come across TypeError: Cannot read property 'state' of undefined error
.
To debug this error, we have to understand the error message. If you type undefined.state
in your browser console, you get the same error message, as shown below.
undefined.state
and the this
keywordThe error message says that you don’t have the state property on an undefined object.
To decode this, we have to understand the this
keyword in JavaScript. this
refers to an object that executes the current bit of JavaScript code.
Let’s look at the following code example.
class Car {setDriveSound(sound) {this.sound = sound;}drive() {return this.sound;}}const car = new Car();car.setDriveSound( "vroom");console.log(car.drive());
We have a Car
class and two methods. The setDriveSound
method initializes the sound
variable, and the drive
method returns the sound
variable.
We instantiate the car object and initialize the sound
variable with “vroom”
. Then, when we call the car.drive()
method, it prints “vroom”
.
The drive()
method in car.drive()
returns this.sound
. Here, this
is a reference to the car
object. This means it returns the sound
variable from the object to which this
is referenced.
To easily identify what the
this
keyword refers to, check what is on the left side of the dot (.
) on which it is called.
In the example above, this
is present in the drive()
method and the drive()
method is called on the car object. In car.drive()
, the car object is to the left of the dot, so this
refers to the car object.
Let’s add some more code to the example above to understand this clearly.
class Car {setDriveSound(sound) {this.sound = sound;}drive() {return this.sound;}}const car = new Car();car.setDriveSound( "vroom");const truck = {sound: "putputput",driveMyTruck: car.drive,};console.log(truck.driveMyTruck());
The code above logs putputput
. Here, this
refers to the truck
object. In the code, we assign the car.drive
callback to a variable that is present in the truck
object.
So, this
can refer to that object when it is called.
What if we assign car.drive
to a variable that is not present in an object and then call it? What do you think will happen?
class Car {setDriveSound(sound) {this.sound = sound;}drive() {return this.sound;}}const car = new Car();car.setDriveSound( "vroom");const drive = car.drive;console.log(drive());
We get “TypeError: Cannot read property ‘sound’ of undefined”
.
We get the error because when we call drive()
, it is a standalone function. this.sound
returns, but the keyword this
refers to nothing.
We can resolve this issue in a number of ways. This shot provides the following three methods.
bind()
methodbind()
fixes the value of the keyword this
.
class Car {constructor(){this.drive = this.drive.bind(this);}setDriveSound(sound) {this.sound = sound;}drive() {return this.sound;}}const car = new Car();car.setDriveSound( "vroom");const drive = car.drive;console.log(drive());
Arrow functions automatically bind their components, so we convert the drive
method in the car
class to an arrow function, as shown below.
class Car {setDriveSound(sound) {this.sound = sound;}drive = ()=> {return this.sound;}}const car = new Car();car.setDriveSound( "vroom");const drive = car.drive;console.log(drive());
We convert the standalone function to an arrow function.
Compare your code with the scenarios above. It should help to debug the error.
class Car {setDriveSound(sound) {this.sound = sound;}drive() {return this.sound;}}const car = new Car();car.setDriveSound( "vroom");const drive = () => car.drive();console.log(drive())