Dealing with Undefined 'this' in React Event Handlers in a Performant Way

By Last Updated: December 11, 2024 4 minutes read

When I’m teaching React to developers, one thing that comes up often is dealing with a common error. But here’s the interesting thing: it’s not running into the error that comes up all the time, its how to deal with it that surprises them. I’ve always wanted to have something to point to that the scenario & why one option is better than others, even though there are plenty of ways to address the issue. That’s what this post is!

Let me start that I’m not the first person to write about this. Spend a few minutes exercising your Google-Fu and you’ll find plenty of others have written about the same thing.

If you’ve worked with React, you’ve probably written a component like this:

export default class HelloWorld extends React.Component<IHelloWorldProps, {}> {
  private foo: string = "some string";

  public render(): React.ReactElement<IHelloWorldProps> {
    return (
      <button type="button" onClick={ this.onClickHandler }>
        trigger problem
      </button>
    );
  }

  private onClickHandler(): void {
    console.log(`Value of 'this'`, this);
    alert(`Value of foo: ${ this.foo }`);
  }
}

In this very simple component, you are accessing a member from the class from the event handler. You think this is pointing to the Sample class, but what you get is a nasty error. TypeError: Cannot read property ‘foo’ of undefined

Why? What the this keyword is bound to depends on where it is defined. In this case, this reverts to its default binding. In the case where you are running in non-strict mode, this is the global window object, but in the case of strict mode, this is undefined.

This post does a good job explaining the difference between default and implicit binding.

So how do you get around this? You have two options, one of which has two flavors.

Option 1: Using Explicit Hard Binding

This is the easiest to understand, but it also isn’t the best option. In this option you explicitly bind the this value to the thing you want it to refer to in a function:

export default class HelloWorld extends React.Component<IHelloWorldProps, {}> {
  private foo: string = "some string";

  constructor(props: IHelloWorldProps) {
    super(props);

    this.onClickHandlerBind = this.onClickHandlerBind.bind(this);
  }

  public render(): React.ReactElement<IHelloWorldProps> {
    return (
      <button type="button" onClick={ this.onClickHandlerBind }>
        fixed with bind
      </button>
    );
  }

  private onClickHandlerBind(): void {
    console.log(`Value of 'this'`, this);
    alert(`Value of foo: ${ this.foo }`);
  }
}

Now, by just adding that single line in the constructor of your component, this will refer to the component within your event handler function.

While this approach works, there are two issues with it:

  1. Isn’t terribly intuitive: It isn’t a great practice to have code somewhere in your project that modifies what other code elsewhere in your project does. You’ve got your event handler in one place, then somewhere completely different in the same file you’ve got a single line that modifies how that code behaves. Sure, it works, but it’s not a great practice.
  2. Isn’t ideally performant: When you bind the current component to the method, you’re binding the whole thing. Let’s say you have ten of these buttons on the page. That means you are binding 10 instances of this component to the function. While it won’t make a big impact in this simple example, some complex components may have more of an impact.

Option 2: Use Arrow Functions

Arrow functions don’t have to worry about this. This is because when you use an arrow function, the event handler is automatically bound to the component instance so you don’t need to bind it in the constructor.

When you use an arrow function you are binding this lexically. According to the definition taken from Stack Overflow, lexically “means that a variable defined outside a function can be accessible inside another function defined after the variable declaration”.

There are two ways you can do this. I prefer to use the public class fields syntax. In this scenario, I like to think that I’ve assigned code to a variable and I’m using the variable in the handler reference. Notice how the declaration of the handler is written. An arrow function is used to assign the code to a variable that’s called as the handler.

export default class HelloWorld extends React.Component<IHelloWorldProps, {}> {
  private foo: string = "some string";

  public render(): React.ReactElement<IHelloWorldProps> {
    return (
      <button type="button" onClick={ this.onClickHandlerArrow }>
        fixed with arrow
      </button>
    );
  }

  private onClickHandlerArrow = (): void => {
    console.log(`Value of 'this'`, this);
    alert(`Value of foo: ${ this.foo }`);
  }
}

The other way is by including the arrow function in the callback. Here, you move the arrow function syntax from the previous snippet from the definition to where it’s used:

export default class HelloWorld extends React.Component<IHelloWorldProps, {}> {
  private foo: string = "some string";

  public render(): React.ReactElement<IHelloWorldProps> {
    return (
      <button type="button" onClick={ (e) => this.onClickHandlerInlineArrow(e) }>
        fixed with arrow
      </button>
    );
  }

  private onClickHandlerInlineArrow(event): void {
    console.log(`Value of 'this'`, this);
    alert(`Value of foo: ${ this.foo }`);
  }
}

Whichever way you prefer, I’d pick at least one of the latter two options for your components.

Branded horizontal divider.