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:
1TypeError: 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.
Let's say that you have just written a small React Component that looks like this:
1class FishSpecies Extends React.Component {
2 constructor(props) {
3 super(props);
4 this.state = {
5 clickCount: 0
6 };
7 }
8
9 onFishClicked() {
10 this.setState(function(prevState, props) {
11 return { clickCount: prevState.clickCount + 1 };
12 });
13 }
14
15 render() {
16 return (
17 <ul>
18 {{ this.props.fish.map(function(fish) {
19 return <Fish name={fish.name} onClick={this.onFishClicked} />
20 })}}
21 </ul>
22 )
23 }
24}
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:
1TypeError: 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.
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!
1class FishSpecies Extends React.Component {
2 constructor(props) {
3 super(props);
4 this.state = {
5 clickCount: 0
6 }
7 }
8
9 onFishClicked() {
10 this.setState((prevState, props) => {
11 return { clickCount: prevState.clickCount + 1 };
12 });
13 }
14
15 render() {
16 return (
17 <ul>
18 {{ this.props.fish.map(fish => {
19 return <Fish name={fish.name} onClick={this.onFishClicked} />
20 })}}
21 </ul>
22 )
23 }
24}
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.
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.