3

Babel is doing its magic, which makes me very confused about what's going on.

What's the difference between foo and bar in this react Component? And when should I use which?

class MyComponent extends Component {
  foo() {
    //...
  }
  bar = () => {
   //... 
  }
}

(My own guess is foo is in the prototype, and bar is in the constructor? Anyways, I don't know what I'm talking about)

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
ZYinMD
  • 3,602
  • 1
  • 23
  • 32
  • 1
    see this: https://github.com/facebook/react/issues/9851 – Anthony Apr 27 '18 at 21:46
  • See this as well: https://babeljs.io/blog/2015/06/07/react-on-es6-plus – Steven Scaffidi Apr 27 '18 at 21:50
  • Does this answer your question? [What's difference between two ways of defining method on React Class in ES6](https://stackoverflow.com/questions/47587470/whats-difference-between-two-ways-of-defining-method-on-react-class-in-es6) – Heretic Monkey Oct 29 '20 at 20:40
  • possible duplicate of [How to use arrow functions (public class fields) as class methods?](https://stackoverflow.com/q/31362292/1048572) – Bergi Oct 29 '20 at 20:43

4 Answers4

7

My own guess is foo is in the prototype, and bar is in the constructor?

That's exactly right.

foo() {}

in this context is a method declaration and the value gets assigned to the prototype. It's equivalent to

MyComponent.prototype.foo = function() {};

bar = ... ;

on the other hand is a class field. This is a shorthand notation for assigning properties to the instance in the constructor:

constructor() {
  this.bar = ... ;
}

And because of how arrow functions work, using class fields with arrow functions basically lets you create "bound" methods.

More on arrow functions: Arrow function vs function declaration / expressions: Are they equivalent / exchangeable?


And when should I use which?

The tl;dr is: Use class field + arrow function when you need a bound function.

When do you need a bound function? Whenever you want this inside the function to refer to a specific value but you don't control how the function is called.

That's mostly the case for event handlers (i.e. functions that are passed to other functions / components) that need to access the component instance (e.g. call this.setState or access this.props/this.state).

You don't have to use it though, you could also bind the method when you need to. However, binding a method only once in the constructor is ideal for React components so that, if the method is passed as event handler, always the same function object is passed.


As noted in another answer, this is not related to React at all. Class fields are likely officially integrated into the language this year.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • Thanks! I have selected your answer to be the correct answer. If it's convenient for you, please consider making some edits to explain why methods like componentDidMount() and render() don't need to be bound functions, and why the code breaks if I change them to arrow functions like render = () => {} – ZYinMD Apr 29 '18 at 20:55
1

In the second example bar is an arrow function.

Until arrow functions, every new function defined its own this value.

For instance, this can be a new object in the case of a constructor.

function Person(age){
  this.age=age;
  console.log(this);
}
let person=new Person(22);

Or this can points to the base object if the function created can be accessed like obj.getAge().

let obj={
  getAge:function(){
    console.log(this);
    return 22;
  }
}
console.log(obj.getAge());

An arrow function does not create its own this, it's just used the this value of the enclosing execution context. In the other hand, arrow function uses this of parent scope.

Mihai Alexandru-Ionut
  • 47,092
  • 13
  • 101
  • 128
1

foo is an instance method of class MyComponent. While bar is an instance property of class MyComponent (that happens to be assigned an anonymous function).

It might make more sense to see it used in the traditional sense...

class MyComponent {

 // instance property
 someProperty = 42;

 // instance method
 someMethod() {}

}

So why use an instance property instead of an instance method?

Instance methods in javascript must be called within the class scope to inherit the class context (this)...

class MyComponent {

 // instance method
 someMethod() {
   const value = this.state.value // ERROR: `this` is not defined
 }

 render() {
   return <div onClick={this.someMethod} />
 }

}

However, since arrow functions inherit their scope, this will be available if you use an arrow function instead

class MyComponent {

 // instance method
 someProperty = () => {
   const value = this.state.value // this works!
 }

 render() {
   return <div onClick={this.someProperty} />
 }

}
Charlie Martin
  • 8,208
  • 3
  • 35
  • 41
  • *"So you cannot do this..."* That's not quit right. It depends on how `someMethod` is called. – Felix Kling Apr 27 '18 at 21:56
  • What do you mean with "just calling it"? `const instance = new MyComponent(); instance.someMethod();` works perfectly fine. Am I not just calling it here? `this.someMethod()` also works fine. Of course there are instances where it does not work. But you are making a blanket statement that it does not work which is not correct. – Felix Kling Apr 27 '18 at 22:00
  • Sorry. You are correct. It depends how you call it. I updated my answer to show how you would call it in a way that causes this issue – Charlie Martin Apr 27 '18 at 22:01
  • Regarding your example of `someProperty = 42`, is it inside the constructor? If yes, why put it in the constructor if it's static? – ZYinMD Apr 27 '18 at 22:34
  • Regarding your example of `someMethod() { const value = this.state.value // ERROR: 'this' is not defined }`, when I use this syntax outside of React, inside any instance method I can always access this.someProperty and this.someMethod() with no problem. I think the problem is not `this`, there seems to be something special about `state` – ZYinMD Apr 27 '18 at 22:41
  • 1) `someProperty` is NOT inside the constructor 2) This issue only arises when you call the function the way I have called it in my example, which is a common pattern in react. The issue is not related to state – Charlie Martin Apr 28 '18 at 05:08
0

Essentially, there is no difference.

This has nothing to do with React, though.


Arrow functions

Arrow functions are a relatively new feature added to Javascript that give you a way of defining functions in a more concise way.

function foo (param1, param2, etc) {
    // do something
}

// becomes

var foo = (param1, param2, etc) => {
    // do something
}

If you have only 1 param, you don't need the parantheses:

function foo (param) {
    // do something
}

// becomes

var foo = param => {
    // do something
}

If there is only 1 expression, and the result is returned, you can omit the {} too!

function foo (param) {
    returns param * 2
}

// becomes

var foo = param1 => param * 2

this is not not updated

In an arrow function, you don't get a new this - it is the same as in the parent block. This can be useful in some cases (when using setTimeout, for example)


JS Classes

In ES6 we can also use the class keyword to define "classes" in Javascript. They still use prototype, though!

function Something (foo) {
    this.bar = foo;
}

Something.prototype.baz = function () {
    return baz * 2
}

// becomes

class Something {
    constructor(foo) {
        this.bar = foo;
    }

    baz () {
        return baz * 2
    }
}

So the constructor is done in constructor() and all the following methods are added to the Prototype. It's nothing but syntactic sugar (but kinda does some of the Prototype heavy lifting for you)

You can use extends!

class SomethingElse extends Something {
    constructor(foo, lol) {
        this.lol = lol
        // Use super() to call the parent's constructor
        super(foo)
    }

    baz () {
        return baz * 2
    }
}
xyhhx
  • 6,384
  • 6
  • 41
  • 64
  • 1
    *"In an arrow function, you don't get a new scope."* Scope and `this` are related but not the same. Arrow functions also have their own scope, they just don't have their own `this` binding. – Felix Kling Apr 27 '18 at 22:03
  • FWIW: `return this;` is also implied for "old stye" constructor functions and you have to call `super(foo)` before you access `this`. – Felix Kling Apr 27 '18 at 22:04