Search code examples
reactjsxmlhttprequest

.bind(this) not working inside xhr addEventListener


.bind(this) is not working inside a response from an xhr request

I've tried to move .bind(this) to almost every place I could think but it's still not working

Ultimately I just need to capture this.obj.id

upload(blob8) {
    var data = new FormData();
    data.append("file", blob8);

    var xhr = new XMLHttpRequest();
    xhr.withCredentials = true;

    xhr.open(
      "POST",
      "URL"
    );
    xhr.setRequestHeader("Authorization", `Bearer ${this.state.accessToken}`); //<--- IS WORKING
    // xhr.setRequestHeader("Content-Type", "application/octet-stream");

    xhr.send(data);

    xhr.addEventListener("readystatechange", function() {
      if (this.readyState === 4) {
        console.log(this.responseText);
        this.obj = JSON.parse(this.responseText);
        console.log(this.obj);
        console.log(this.obj.id);
        this.uploadMetaData(this.obj.id).bind(this);  // <------- NOT WORKING
      } else {
        console.log("there is an error here");
      }
    });
  }

Solution

  • Edit:

    I missed that the callback is referencing two different this contexts. Two possible ways to solve this:

    upload() {
        var that = this;
        xhr.addEventListener("readystatechange", function() {
            that.uploadMetaData(this.obj.id); 
        });
    }
    

    or

    upload() {
        var boundUploadMetaData = this.uploadMetaData.bind(this);
        xhr.addEventListener("readystatechange", function() {
            boundUploadMetaData(this.obj.id); 
        });
    }
    

    Both will call uploadMetaData with upload()'s this while allowing the callback to still reference the xhr object's this.

    Alternatively, I don't think you need to reference the xhr object's this at all:

    xhr.addEventListener("readystatechange", function() {
        if (xhr.readyState === 4) {
            var res = JSON.parse(xhr.responseText);
            this.uploadMetaData(res.id);
        } else {
            console.log("there is an error here");
        }
    }.bind(this));
    

    Original answer:

    Assuming this isn't a method for an ES2015 class (it sounds like it isn't if the this reference in upload() is correct), the callback for "readystatechange" will need to be bound to the outer method's (upload()) this.

    You should be able to do either of the following:

    xhr.addEventListener("readystatechange", function() {
      // callback body   
    }.bind(this));
    

    or, if you're able to use ES2015 syntax, then

    xhr.addEventListener("readystatechange", () => {
      // callback body   
    });
    

    since arrow function expressions bind this by their lexical scope.