Apr 25 '22 ~ 7 ~ 3 mins

Use Arrow function, () => {}, to correctly bind this

As part of my role working for AG Grid I get to see a number of repeated issues. One such issue that I have seen multiple times is this.

this.props is undefined in a callback but I know that it does have a value in my component!

What is this?

Let's look at an example of when this can easily occur. Below is a section of the code for a Bar Chart which has added a click event listener.

This example is written in React but you will experience the same in Angular, Typescript and any Class based code.

class ChartExample extends Component {
  constructor(props) {
    super(props);

    this.state = {
      options: {
        series: [
          {
            listeners: {
              nodeClick: function (event) {
                // Do something with our props
                console.log("Props are:", this.props)
              },
            },
          },
        ],        
      },
    };
  }

  render() {
    return (
      <div className="wrapper">
        <AgChartsReact options={this.state.options} />
      </div>
    );
  }
}

render(<ChartExample test={'Test Value'} />, document.querySelector('#root'));

The key part of this code is the following section where we define our listeners.

 listeners: 
  {
    nodeClick: function (event) {
        // Do something with our props
        console.log("Props are:", this.props)
    }
  }

If our component receives props then we would expect that these would be logged to the console. However the above code logs undefined when a node is clicked. You can confirm this for yourself in this Plunker.

this.props is undefined within function callback

The arrow function fix

If we make what seems like a purely syntax change, swapping out the function definition for the equivalent arrow format, then try again, suddenly our props are now correctly logged to the console! Updated Plunker

listeners: {
             nodeClick: (event) => {
               // props are defined
               console.log("Props are:", this.props)
             },
           },

this.props correctly defined within an arrow function callback

So what is going on here and why does using the syntax (event) => {} suddenly make this.props have the correct value!?

This binding with arrow function

The short answer to why this is works is that by default arrow functions bind this to the value in their surrounding scope. So in our example this will be correctly bound to our component class meaning that this.props will be correctly defined. When we use a standard function definition this is not bound to our component but instead will have the value of the class where our event was fired.

If you want to dive deeper into this topic I would recommend Understanding Arrow Functions in JavaScript

Alternative Fix with bind

It is worth mentioning that you can also fix the original code by explicitly binding this when you define the callback.

listeners: {
              nodeClick: function (event) {
                console.log("Props are:", this)                
              }.bind(this),
            },

Conclusion

If you ever find yourself surprised that this.props, or for that matter, this.anyClassProp is undefined when you are expecting it to contain a certain value then you could check the context in which you are using this. You may find that simply updating a function definition to an arrow function resolves your issue.

As a result of these issues I have made a large update to all our AG Grid examples so that they use arrow functions by default to help avoid confusion for our users.


Headshot of Stephen Cooper

Hi, I'm Stephen. I'm a senior software engineer at AG Grid. If you like my content then please follow / contact me on 🦋 Bluesky or 𝕏 (X) and say hello!