2

I am reading the air-bnb javascript style guide and it says this should not be done:

23.5 Don’t save references to this. Use arrow functions or Function#bind.

// bad
function foo() {
  const self = this;
  return function () {
    console.log(self);
  };
}

// bad
function foo() {
  const that = this;
  return function () {
    console.log(that);
  };
}

// good
function foo() {
  return () => {
    console.log(this);
  };
}

I already did some research and found two answers here in stackoverflow but the accepted answer in one of them is that this cannot be circumvented:

ES6 avoid that/self

and the other one suggests using bind, but it suggested using a generator function.

Nested reference to `this` in ES6 classes

I am building a ReactJS app and I need to access setState from within a firebase Promise.

   saveSessionToDB = () => {
        const that = this;
        db.collection("sessions")
          .add({
            props: MyProps
          })
          .then(function(docRef) {
            console.log("Session written with ID: ", docRef.id);
            that.setState({ sessionID: docRef.id });
          })
          .catch(function(error) {
            console.error("Error adding document: ", error);
            that.setState({ sessionError: error });
          });
}

I tried

const {setState} = this;

and then

setState({sessionID:docRef.id});

but I get an error.

I tried binding the saveSessionToDB in the constructor to no avail, even if it's an arrow function.

Is there another way of doing this or should I just accept that sometimes I will have to still write const that = this?

Jack Bashford
  • 43,180
  • 11
  • 50
  • 79
guergana
  • 374
  • 5
  • 19
  • 1
    Why don't you just use arrow functions? – Roberto Zvjerković Jul 03 '19 at 10:01
  • You might remind them of [*Hitchens's razor*](https://en.wikipedia.org/wiki/Hitchens%27s_razor). *Why* is it bad? – RobG Jul 03 '19 at 10:01
  • @ritaj it is an arrow function. – guergana Jul 03 '19 at 10:02
  • @RobG pretty much all of the ESLint rules don't have "proof" of being bad but are mostly coding standards. It's good to keep them, bad to deviate, even if you can achieve the same thing with different code/syntax/formatting. – VLAZ Jul 03 '19 at 10:03
  • @guergana not what you pass to `.then` – VLAZ Jul 03 '19 at 10:03
  • 1
    `.then(function(docRef) {} )` this is not an arrow function. – Roberto Zvjerković Jul 03 '19 at 10:03
  • yes, just noticed. thanks! – guergana Jul 03 '19 at 10:06
  • @VLAZ—yes they do, see the [documentation](https://eslint.org/docs/rules/). ESLint doesn't prescribe what's "good" or "bad", it's just a bunch of rules that can be used for checking coding style, i.e. just because it has a rule doesn't mean enforcing that rule is a good idea in general. The documentation includes suggestions of when not to use particular rules (e.g. [*no-invalid-this*](https://eslint.org/docs/rules/no-invalid-this)). – RobG Jul 03 '19 at 10:18
  • @RobG that is...what I said. ESLint rules is just for enforcing coding standards. Ergo, aren't "good" or "bad". – VLAZ Jul 03 '19 at 10:21
  • 1
    @VLAZ—sure, missed your point. :-) But it reinforces that Airbnb should explain why *that* and *self* should not be used (I really don't care, I just think they should explain why they insist on a particular coding style). – RobG Jul 03 '19 at 10:25

3 Answers3

4

You have to use arrow functions for the then and catch too:

saveSessionToDB = () => {
  db.collection("sessions")
    .add({ props: MyProps })
    .then((docRef) => {
      console.log("Session written with ID: ", docRef.id);
      this.setState({ sessionID: docRef.id });
    })
    .catch((error) => {
      console.error("Error adding document: ", error);
      this.setState({ sessionError: error });
    });
}

The reason why arrow functions are useful is that they don't have a this context of their own (or an arguments), so they adopt the this from their outer lexical environment. Which is why you can use the component's this inside the arrow function.

Andy
  • 61,948
  • 13
  • 68
  • 95
1

Use arrow functions for the internal functions:

.then(docRef => {
  console.log("Session written with ID: ", docRef.id);
  this.setState({ sessionID: docRef.id });
})
.catch(error => {
  console.error("Error adding document: ", error);
  this.setState({ sessionError: error });
});

Or use bind:

.then(function(docRef) {
  console.log("Session written with ID: ", docRef.id);
  that.setState({ sessionID: docRef.id });
}.bind(this))
.catch(function(error) {
  console.error("Error adding document: ", error);
  that.setState({ sessionError: error });
}.bind(this));
Jack Bashford
  • 43,180
  • 11
  • 50
  • 79
1

In promises that are used on screens/components and even, mostly outside of react syntax in the then() method and catch() you usually send an arrow function (There is nothing worth in the fetch context) so you keep the context from outside (In this case the React.Component context).

In your case with just two fixes (arrow function, and that to this) everything works:

saveSessionToDB = () => {
        const that = this;
        db.collection("sessions")
          .add({
            props: MyProps
          })
          .then((docRef) => { // arrow function
            console.log("Session written with ID: ", docRef.id);
            this.setState({ sessionID: docRef.id }); // that => this
          })
          .catch((error) => { // arrow function
            console.error("Error adding document: ", error);
            this.setState({ sessionError: error }); // that => this
          });
}
Josep Vidal
  • 2,580
  • 2
  • 16
  • 27