0

I'm trying to load some JSON into a JavaScript class and assign some properties from the JSON to properties on an instance of the class, but I'm having trouble with 'this' and scope. I'm pretty sure the solution involves arrow functions, but I can't figure out how to piece things together.

Here's what I have so far:

class Test {

    constructor(url) {
        this.loadJSON(url, this.init);
    }

    loadJSON(url, callback) {
        let xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        xhr.responseType = 'json';
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4 && xhr.status === 200) {
                callback(xhr.response);
            }
        };
        xhr.send(null);
    }

    init(response) {
        console.log(response.description);
        // this is undefined
        this.description = response.description;
    }

}

In the init() method, the console.log works as I'd expect, but I'm having trouble assigning anything to the object - I just keep getting 'this is undefined.'

safetycopy
  • 554
  • 4
  • 15
  • 1
    Here is a good explanation : https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-inside-a-callback – NanoPish Feb 26 '18 at 22:53
  • 1
    Use `this.loadJSON(url, this.init.bind(this));` – 4castle Feb 26 '18 at 23:06
  • 1
    @NanoPish That looks great - thanks! – safetycopy Feb 26 '18 at 23:23
  • @4castle That seems to work - thanks! I will look into `bind` a bit more. – safetycopy Feb 26 '18 at 23:24
  • 1
    You really shouldn't use a `class` here. Its method never do anything useful, they're not supposed to be called from the outside. And [you should never start asynchronous things in your constructor](https://stackoverflow.com/a/24686979/1048572). – Bergi Feb 26 '18 at 23:47
  • @Bergi The class shown as above is just a test. The real class I'm working on does a lot more. I will read the page you linked to, though, as the asynchronous load is causing me problems elsewhere (I'm actually working with p5.js). – safetycopy Feb 26 '18 at 23:54
  • 1
    @Bergi Your comment really helped me rethink things. The link was very useful, particularly the bit about "Make the data ifself a parameter to your constructor, instead of telling the constructor how to fetch the data." I've restructured things now and it makes much more sense. Thanks! – safetycopy Feb 27 '18 at 00:10

1 Answers1

1

Either modify your callback(xhr.response); code like below :

callback.call(this, xhr.response);

Or, modify this.loadJsON code like below :

this.loadJSON(url, this.init.bind(this)); 

Here is the full solution :

class Test {


        constructor(url) {
            this.that = this;
            this.loadJSON(url, this.init);
//Or, this.loadJSON(url, this.init.bind(this)); In that case you dont have to modify "callback(xhr.response);" code
        }

        loadJSON(url, callback) {
            let xhr = new XMLHttpRequest();
            xhr.open('GET', url);
            xhr.responseType = 'json';
            xhr.onreadystatechange = function() {
                if (xhr.readyState === 4 && xhr.status === 200) {
                    callback.call(this, xhr.response);
                }
            };
            xhr.send(null);
        }

        init(response) {
            console.log(response.description);

            this.description = response.description;

            console.log(this.description);

        }

    }

    let t = new Test('MY URL');
sudip
  • 2,781
  • 1
  • 29
  • 41