Author avatar

Zachary Bennett

Use ES6 Arrow Functions to Resolve "TypeError: Cannot read property '<your property name>' of undefined"

Zachary Bennett

  • Oct 13, 2020
  • 5 Min read
  • 61 Views
  • Oct 13, 2020
  • 5 Min read
  • 61 Views
Web Development
Front End Web Development
Client-side Frameworks
React

Introduction

If you are a React developer or are just learning to program in JavaScript, you might have run into this dreaded error while trying to read a property off of the this keyword:

1
TypeError: Cannot read property '<your property name>' of undefined

If you run into this error while writing React, the odds are high that you are trying to use a function within another function and are trying to access the this keyword within the nested function. But why does this happen?

This guide will dive into how function scope works in JavaScript. You will learn why this error occurs and what you can do to fix it.

Let's get started.

The Problem and an Initial Solution

Let's say that you have just written a small React Component that looks like this:

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 FishSpecies Extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            clickCount: 0
        };
    }

    onFishClicked() {
        this.setState(function(prevState, props) {
            return { clickCount:  prevState.clickCount + 1 };
        });
    }

    render() {
        return (
            <ul>
                {{ this.props.fish.map(function(fish) {
                    return <Fish name={fish.name} onClick={this.onFishClicked} />
                })}}
            </ul>
        )
    }
}
jsx

At first, this component looks pretty straightforward. All this component does is receive a list of fish objects via props and render a Fish component for each one, passing down a couple of props to each fish. However, if you create this component and add to a real React app, it will fail. You will see an error that looks like:

1
TypeError: Cannot read property 'onFishClicked' of undefined

Oh no, there it is—the dreaded TypeError! So, why is this happening? This error informs you that this is undefined. Specifically, onClick={this.onFishClicked} fails for this reason. The reason the code is unable to access this here is because of how scope works in JavaScript.

Under the hood, functions are objects in JavaScript. When you create a function in JavaScript, it gets its own scope depending upon the context in which it is instantiated. In this case, there actually is no context, and so this is actually undefined! Essentially, the code is running in strict mode within the React framework and so the global context is not used in favor of no context at all. Check out these docs for more info.

So how can you fix this? Well, you have a number of options. In the following section will demonstrate the best and most modern means of fixing this error.

The ES6 Solution

So you've diagnosed the problem. You need to make sure that your functions have access to the this context of your class component! To do this, you can use ES6 arrow functions.

Apart from making your code more succinct and readable, arrow functions serve another purpose. It is not immediately obvious, but arrow functions play a big part in how context is passed down to functions. Essentially, when you declare a function using the arrow syntax you are telling the JavaScript code to bind the current context of this into the new function that is being created. This is not immediately obvious because this binding is done implicitly as compared to the explicit means of binding this by using the .bind method.

Let's see how you can use arrow functions to fix your code!

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 FishSpecies Extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            clickCount: 0
        }
    }

    onFishClicked() {
        this.setState((prevState, props) => {
            return { clickCount:  prevState.clickCount + 1 };
        });
    }

    render() {
        return (
            <ul>
                {{ this.props.fish.map(fish => {
                    return <Fish name={fish.name} onClick={this.onFishClicked} />
                })}}
            </ul>
        )
    }
}
jsx

Voila! It was a simple change to both of the function declarations above, but now your dreaded TypeError is gone because the function you are passing into Array.map within the render function has access to your component's this context. And this was all accomplished simply by changing how you declared your function.

Conclusion

Arrow functions are a powerful means of binding the current context of this into nested functions. Unfortunately, this major benefit is implicit and so you would never know about it just by looking at its usage. Most, if not all, of your function declarations within your React components should be declared as arrow functions. Doing this will help you to avoid confusion when it comes to the context of this and what it gets bound to.

For more information on the this keyword in relation to function scope, please check out the Mozilla JavaScript developer documentation.

1