2

I understand (I think) that arrow functions in ES6 use Lexical this, but I'm not sure why a function called by a fat arrow function would have this set as undefined.

What do I have to do to be able to call this.setResult in handleAuthResult? I do not want to use the old function () {}.bind(this) if I don't need to.

"use strict";

class Example {
    constructor() {
        this.checkAuth();
    }

    checkAuth() {
        document.write("Checking auth now. ");
        var iid = setInterval(() = > {
            if (true) { // Have to do some dumb check here
                clearInterval(iid);
                this.authenticate(this.handleAuthResult)
            }
        }, 500);
    }

    authenticate(callback) {
        callback({
            value: true
        });
    }

    handleAuthResult(result) {
        document.write(`The result is ${result.value}.`);
        this.setResult(result, this.loadThePage)
        // ^ `this` is undefined here. How can I ever set the result?
    }

    // Another asynchronous thing
    setResult(result, callback) {
        callback();
    }

    loadThePage() {
        document.write("The code never gets here, but the page should load now. ");
    }
};
var example = new Example();

Thanks! https://jsfiddle.net/vujev4dj/

Edit: in my defense of this being marked as duplicate, the behaviour I expected does work in the following fiddle, which is why I expected to not have to use the bind function on this.handleAuthResult in ES6: https://jsfiddle.net/m9e7j4ds/

Abe Fehr
  • 729
  • 9
  • 23
  • The reason it worked in the fiddle is because it uses `React.createClass()` instead of using ES6 classes (extending `React.Component`). `React.createClass()` binds all methods, which doesn't happen when using ES6 classes. – ArneHugo Mar 06 '16 at 15:28
  • @bergi I don't agree that this is a duplicate of the question you linked, because it is not about ways to bind methods to ES6 class instances. Especially, making an arrow function directly one the class is not mentioned in the answers. I believe that is what the asker was looking for, although the question could be more well posed. – ArneHugo Mar 06 '16 at 15:45

3 Answers3

4

When you call

this.authenticate(this.handleAuthRequest);

The this becomes lost

You could do

this.authenticate(this.handleAuthRequest.bind(this));

Or

this.authenticate(() => this.handleAuthRequest());

Overall the code is pretty messy tho and a lot of parts don't make any sense to me. Specifically callback({value: true}) is pretty weird. Anyway, If you have more specific questions, I'm happy to help.

Mulan
  • 129,518
  • 31
  • 228
  • 259
  • 1
    Good point about your third option using another arrow function. – T.J. Crowder Mar 06 '16 at 10:49
  • Thanks for the great suggestions. I agree that the code is messy, but it's actually a super simplified version of this JavaScript quickstart provided by Google where I replaced their functions with my own functions that took callbacks: https://developers.google.com/google-apps/calendar/quickstart/js#step_1_turn_on_the_api_name – Abe Fehr Mar 06 '16 at 10:57
  • 1
    Thanks @TJ. I often prefer to use another arrow function instead of leaning on `Function.prototype.bind` – Mulan Mar 06 '16 at 11:00
  • A better use of arrow functions would be to put it directly on the class. Then it is always bound to the instance of the class. – ArneHugo Mar 06 '16 at 15:36
0

in checkAuth, you should use

this.authenticate(this.handleAuthResult.bind(this))

that's because when calling callback({value: true}); there is not this binded

wong2
  • 34,358
  • 48
  • 134
  • 179
  • Another way is to define callback methods as arrow functions, which, when applied to classes, are always bound to the class they are on. This question specifically asks about arrow functions. – ArneHugo Mar 06 '16 at 15:34
0

this is undefined in handleAuthResult because handleAuthResult isn't an arrow function and thus doesn't have lexical this. It's a normal prototype function (loosely, "method") which means that this is defined by how it's called.

The way you're calling it:

authenticate(callback) {
    callback({
        value: true
    });
}

doesn't set this to anything specific, so this is undefined (because you're in strict mode).

To fix it, either pass this into authenticate and use it:

this.authenticate(this.handleAuthResult, this)

and

authenticate(callback, thisArg) {
    callback.call(thisArg, {
        value: true
    });
}

or use Function#bind to create a function that, when called, will call handleAuthResult with the correct this:

this.authenticate(this.handleAuthResult.bind(this))

or for completeness, naomik points out a third option which is quite elegant: Using another arrow function:

this.authenticate(() => this.handleAuthRequest())
Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 1
    I see that you appended @naomik's solution here as well. Your response is overall very concise and explains why I was confused. I've accepted your answer :) – Abe Fehr Mar 06 '16 at 11:14