4

I see this a lot in ES6 React code

class Foo extends React.Component {
  bar = () => {
    console.log("bar")
  }

  baz() {
    console.log("baz")
  }
}

Seems like they both define methods bar and baz on Foo but how are they different.

Bergi
  • 513,640
  • 108
  • 821
  • 1,164
User314159
  • 6,447
  • 8
  • 31
  • 55

3 Answers3

9

The declarations differ in how the function are written and the context of this,

In the first syntax

bar = () => {
    console.log("bar")
  }

the function is written using Arrow function syntax.

An arrow function does not have its own this; the this value of the enclosing execution context is used. Hence this keyword inside this function will refer to the context of the React class

However the second declaration

baz() {
    console.log("baz") 
}

is a simple function and this keyword in this function refers to the context of the function itself.

So when you try to access React class Properties/functions like this.state or this.setState you will get an error in the second case(if you haven't used binding anywhere else for this function(example constructor)) whereas it would work in the first case since for an arrow function, this means the same thing within the function body as it does outside of it. Which means that if you use arrow functions within your component’s custom functions, they can use this and this.setState with no surprises.

Check this answer on why you need to bind functions in React classes

Shubham Khatri
  • 211,155
  • 45
  • 305
  • 318
  • `you will get an error in the second case(if you have used binding...`. I guess you mean `you will get an error in the second case(if you have not used binding...` – Prakash Sharma Dec 01 '17 at 06:27
  • @Prakashsharma, yes that was a typo on my behalf, updated it – Shubham Khatri Dec 01 '17 at 06:27
  • 1
    Also defining a method using arrow function, actually makes it an instance property that happens to be a function, rather than adding it as a method to the prototype, this is important distinction if you ever want to subclass your component, you wont be able to access `super.bar()` it in the subclass – alechill Dec 01 '17 at 11:30
  • Thanks for the explanation. I'm wondering how render() method can use the scope of `this` of component. As `this.state` works in `render()` method even though it's not a arrow function. Any thoughts on this? – Malleswari Feb 25 '18 at 10:17
  • @Malleswari, render and lifecycle methods are already binded to the correct context in their implementation and hence you dont need to bind them – Shubham Khatri Feb 25 '18 at 16:43
1

The previous answers are absolutely correct and are why you would want to be using arrow functions in react classes.

I just wanted to also point out a subtle difference that is a potential pitfall of their use in a class to avoid surprises...

Arrow functions defined on a class are added to the instance as a property that just so happens to be a function, whereas defining not as an arrow function will add the function as a method to the class's prototype.

In React components that will never be extended this is fine, but if the case arises you want to subclass a component, you will not be able to override an arrow function expecting to be able to call the base class via super, you can only overwrite it in its entirety

class Button extends React.PureComponent {

    // class method - overridable in subclass
    pressMethod: function(): void {
      console.log('beep')
    }

    // instance property functions - only overwriteable in subclass
    pressArrow = (): void => {
      console.log('boop')
    }

    pressArrowTwo = (): void => {
      console.log('blip')
    }

  }


  class BigRedButton extends Button {

    // class method - overides subclass one, 
    // can call superclass method via `super`
    pressMethod: function(): void {
      super.pressMethod() // prints 'beep' to console
      console.log('BOOOOOOOOM!!!')
    }

    // instance property function
    // cannot call superclass via `super` as lambda's have no prototype
    pressArrow = (): void => {
      super.pressArrow() // TypeError - not a function
      console.log('BOOOOOOOOM!!!')
    }

    // completely overwrites instance property of same name in subclass
    // doesn't try to access prototype so won't error but is limited
    pressArrowTwo = (): void => {
      console.log('BOOOOOOOOM')
    }

  }
alechill
  • 3,560
  • 13
  • 22
0

For simplicity, both are equal:

  1. bar = () => { ... }
  2. this.bar =this.bar.bind(this)
Yossi
  • 415
  • 2
  • 9