1

I understand the concept of binding event handlers in order for them to get the proper reference to this. My question is, why does referencing a class's method not provide it the proper this?

Do ES6 classes not automatically provide this on class methods?

class MyComponent extends Component {
  clickHandler(event) {
    console.log('this', this);
  }

  render() {
    return <button onClick={this.clickHandler}>Button</button>;
  }
}

As expected, the above code will print null for the value of this. However, explicitly calling the handler will give the correct value, i.e. <button onClick={event => this.clickHandler(event)} />. Why does the handler have the correct reference to this using the arrow function?

I can hypothesize a bunch of answers, but I'd like the correct one.

Alan Thomas
  • 1,006
  • 2
  • 14
  • 31

1 Answers1

1

Why do event handlers not get the right handle to this?

They do! They get a reference to the element you hooked the event on, as normal.

Do ES6 classes not automatically provide this on class methods?

They don't, no; ES2015 ("ES6") classes use the same fundamental prototypical inheritance and this assignment as ES5 and before, with different syntax (and a couple of new features).

However, explicitly calling the handler will give the correct value

Right, for the same reason that in this code, example is called with this referring to x:

x.example();

...but in this code, example is called with this being undefined (in strict mode; it would be the global object in loose mode):

let e = x.example;
e();

Using class doesn't change the need for this management.

To make this to refer to the component class, you need to either bind this to it, or use an arrow function that closes over the this to use:

Binding (a fairly common pattern):

class MyComponent extends Component {
  constructor(...args) {                               // ***
    super(...args);                                    // ***
    this.clickHandler = this.clickHandler.bind(this);  // ***
  }                                                    // ***

  clickHandler(event) {
     console.log('this', this);
  }

  render() {
    return <button onClick={this.clickHandler}>Button</button>;
  }
}

Using an arrow function instead (also fairly common):

class MyComponent extends Component {

  clickHandler = event => {                            // ***
     console.log('this', this);
  };                                                   // ***

  render() {
    return <button onClick={this.clickHandler}>Button</button>;
  }
}

That latter option assumes you're transpiling with support for the upcoming public class fields, which aren't standard yet. (The proposal is still at stage 2 as of this writing.) Instead of creating a property on the prototype, it creates an instance field with an arrow function, which closes over this (which is a reference to the component instance).

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875