2

I have a snippet like that, which works:

class Button extends React.Component {
    state = { counter : 0 }
  handleClick = () => {
    this.setState({
        counter: this.state.counter+1
    })
  }

  render() {
    return (
        <button onClick={this.handleClick}>
          {this.state.counter}
        </button>
    )
  }
}

Above, the handleClick function is an arrow one. Below, instead I'm trying to use default function (from ES5):

 class Button extends React.Component {
    state = { counter : 0 }
  handleClick = function() {
    this.setState({
        counter: this.state.counter+1
    })
  }

  render() {
    return (
        <button onClick={this.handleClick}>
          {this.state.counter}
        </button>
    )
  }
}

ReactDOM.render(<Button/>, mountNode)

The problem that the second snippet don't work properly is in function's writing way? Or somewhere else? React js do not handle default functions?

Mibac
  • 8,990
  • 5
  • 33
  • 57
  • possible duplicate: https://stackoverflow.com/questions/34361379/arrow-function-vs-function-declaration-expressions-are-they-equivalent-exch – Raghav Garg Sep 10 '17 at 12:30
  • What do you mean by not working properly? If you are having any errors please edit your question and add it. – bennygenel Sep 10 '17 at 12:56
  • Any error, just does not work - counter state does not react on clicks. –  Sep 10 '17 at 13:09

4 Answers4

2

When you write this:

class Example {
  handleClick = (e) => {
    this.doStuff(e);
  }
}

it's being transpiled by Babel to something like this:

var Example = function Example() {
  var _this = this;

  this.handleClick = function (e) {
    return _this.doStuff(e);
  };
};

The method handleClick has access to the object's instance because the constructor stores the instance in the variable _this and the method forms a closure where _this is available in its lexical environment. It uses _this instead of the keyword this, because when called by an event listener that doesn't know about the original object, this would have the value window (or undefined if strict mode is enabled).



Now, when you use a regular function instead of an arrow function, like so:
class Test {
  handle = function (e) {
     return this.doStuff(e);
  }
}

It's transpiled to something like this instead:

var Test = function Test() {
  this.handle = function (e) {
    return this.doStuff(e);
  };
};

As you can see, it's now trying to access the instance via the keyword this, which won't be the object instance when the method is called from an event listener, so it won't work as expected.



If you're a dreamer like me and you like thinking about a world where the "class properties transform" is part of the actual spec and we're all using ES2xxx natively, you can think of the arrow function version as equivalent to the following:

class Example {
  constructor() {
    this.handleClick = (e) => {
      this.doStuff(e);
    }
  }
}

You can see here that it's creating an arrow function in the constructor, and since arrow functions take their context (i.e. the value of this) from the surrounding scope, and the scope here is the constructor itself, this will have the appropriate value, no matter where the method is called.

philraj
  • 820
  • 6
  • 13
1

In ES6, this has a different meaning when used in an arrow function. this in an arrow function refers to the instance of the class it is wrapped in where this in a usual function refers to the instance of the function itself and not the class. This is termed as Lexical this in the ES6 specs

Therefore, in your case, the this.state.counter+1 in the normal function will be undefined. If you must use function, then I guess you should do this:

class Button extends React.Component {
  state = { counter : 0 }
  self = this
  handleClick = function() {
    self.setState({
      counter: self.state.counter+1
    })
  }
  ...
}

ReactDOM.render(<Button/>, mountNode)

IMO, binding is better though

16kb
  • 889
  • 9
  • 17
0

You need to bind method to your class:

onClick={this.handleClick.bind(this)}
Artem Mirchenko
  • 2,140
  • 1
  • 9
  • 21
-2

In your ES5 code replace the following

onClick={this.handleClick}

with

onClick={this.handleClick()}
KeshavDulal
  • 3,060
  • 29
  • 30
MukulSharma
  • 231
  • 2
  • 15
  • I tried that - "RangeError: Maximum call stack size exceeded" –  Sep 10 '17 at 12:53
  • 1
    This is wrong. You pass a function as a click handler, not a function call. There's an important difference. You're telling React's synthetic event system which function to call when a click event is registered. – philraj Sep 10 '17 at 13:57
  • @Adam It was maxing out the call stack because it was calling `handleClick` on every render, and since `handleClick` calls `setState`, it was triggering a new render, creating an infinite loop. – philraj Sep 10 '17 at 14:36