0
votes

I have the react component below. Why this is not defined when changeNameTwo is called?

See jsbin: http://jsbin.com/nuhedateru/edit?js,output

Then why it works in a typical ES6 Class? See jsbin: http://jsbin.com/kiyijuqiha/edit?js,output

class HelloWorldComponent extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      name : 'yolo'
    }
  }

  changeName = () => {
    this.setState({name: 'another yolo'});
  }

  changeNameTwo() {
    this.setState({name: 'another yolo'});
  }

  render() {
    return (      
      <div>
        <h1>Hello {this.props.name}</h1>
        <p>Name: {this.state.name}</p>
        <button onClick={this.changeName}>Change Name</button>
        <button onClick={this.changeNameTwo}>Change Name 2</button>
      </div>
    );
  }
}

React.render(
  <HelloWorldComponent name="ES2015/ES6"/>,
  document.getElementById('react_example')
);
2
Basically a combo of the two answers below, React doesn't have auto-binding built into its class model, though you have plenty of options available to help you with this. Secondly, the two examples given are doing two different things, you could technically just call methods directly inside React components as well, but it will quickly cause invariant violations when state gets mutated during render cycles: codepen.io/bullerb/pen/940776ef6eb81c4aa3b64b9acc8ce89cBrad Colthurst
"why it works in a typical ES6 Class?" There is a big difference between the two pieces of code: In the first one you are binding an event handler (you are not calling the function directly). In the non-React example you are calling the function directly. The value of this depends on how the function is called.Felix Kling

2 Answers

3
votes

One is a DOM event, the other you are directly calling. By default the context of a click event is the element that was clicked.

It is often desirable to reference the element on which the event handler was fired, such as when using a generic handler for a set of similar elements.

When attaching a handler function to an element using addEventListener(), the value of this inside the handler is a reference to the element. It is the same as the value of the currentTarget property of the event argument that is passed to the handler. (MDN - https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)

Your methods have a default context of the instance, but only if the context is not specified (a normal function call as opposed to a .call or .apply. Thus animal.speak() will by default have the correct context.

When a function is called as a method of an object, its this is set to the object the method is called on. (MDN - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this)

To summarise, the click event sets a specific context which overrides that. As you probably know you can solve it with .call/.bind/.apply or onClick={(e) => this.changeName(e)}. Probably an implementation today wouldn't do that, but I imagine they have to keep it for compatibilities' sake.

2
votes

When you use extend Component instead of React.createClass you lose autobinding feature. Binding to this is not a class itself, it’s undefined. It’s default JavaScript behavior and is quite expected.

What you can do about it

Method 1. Use Function.prototype.bind().

export default class CartItem extends React.Component {
    render() {
        <button onClick={this.increaseQty.bind(this)} className="button success">+</button>
    }
}

Method 2. Use a function defined in the constructor.

export default class CartItem extends React.Component {

    constructor(props) {
        super(props);
        this.increaseQty = this.increaseQty.bind(this);
    }

    render() {
        <button onClick={this.increaseQty} className="button success">+</button>
    }
}

Consider using this method over Method 1. Every time you use bind it actually creates a copy of a function. So it's much better to use it once in constructor then every time when your component calls render method

Method 3. Use an arrow function in the constructor.

export default class CartItem extends React.Component {

    constructor(props) {
        super(props);
        this.increaseQty = () => this.increaseQty();
    }

    render() {
        <button onClick={this.increaseQty} className="button success">+</button>
    }
}

Method 4. Use an arrow function and the class properties proposal.

export default class CartItem extends React.Component {

    increaseQty = () => this.increaseQty();

    render() {
        <button onClick={this.increaseQty} className="button success">+</button>
    }
}

Class properties are not yet part of current JavaScript standard. But your are free to use them in Babel using corresponding experimental flag (stage 0 in our case).

You can see original article here