Cannot call setState in callback

782 views Asked by At

I am new to React, so I know I might not be doing everything right.

I am trying to call setStatus inside of a callback for onreadystatechange but getting an error in the browser. The code does an AJAX call to the server to see if a proposed username is already in the database. Upon return the onreadystatechange is executed. There I am trying to set the new error message for the username if it already exists.

Here is my state data:

const initialState = {
  firstname: "",
  lastname: "",
  username: "",
  password: "",
  email: "",
  firstnameError: "",
  lastnameError: "",
  usernameError: "",
  passwordError: "",
  emailError: ""
};

class SignUpForm extends Component {
  constructor() {
    super();

    this.state = initialState;

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.checkUserName = this.checkUserName.bind(this);
    this.checkEmail = this.checkEmail.bind(this);
    this.validateForm = this.validateForm.bind(this);
  }

This is where I update state in a change handler for fields in the form:

handleChange(e) {
    let target = e.target;
    let value = target.value;
    let name = target.name;

    if (name === "username") {
      value = value.toUpperCase();
    }

    this.setState({
      [name]: value
    });
  }

This is the routine where I am getting an error in the browser. The lines where the error occurs is marked with a comment on the line below.

checkUserName() {
    let usernameError = "";
    let element = "";
    let checkNameResponse = "";
    let checkNameMessage = "";
    let request = "";

    let url = "";
    let userName = "";
    let requestData = "";
    let checkNameResponseJSON = "";

    request = new XMLHttpRequest();
    if (request == null) alert("Unable to create checkDBForUSerNameRequest");
    else {
      url = "/php/CheckUsername.php";
      userName = escape(document.getElementById("username").value);
      requestData = JSON.stringify({
        username: userName
      });

      request.onreadystatechange = function() {
        if (request.readyState === 4 && request.status === 200) {
          checkNameResponseJSON = JSON.parse(request.responseText);
          checkNameResponse = checkNameResponseJSON.returncode;
          checkNameMessage = checkNameResponseJSON.message;

          element = document.getElementById("SignupIcon");
          element.className = "displayIcon";

          if (checkNameResponse === 0) {
            element = document.getElementById("SignupIconFile");
            element.src = "/images/YesButton.png";
            element.alt = "Available";
            this.setState({ usernameError: "" });
          } else {
            element = document.getElementById("SignupIconFile");
            element.src = "/images/NoButton.png";
            element.alt = "Not Available";
            usernameError = checkNameMessage;
            this.setState({ usernameError: usernameError });  // BROWSER ERROR
          }
        }
      };

      request.open("POST", url, true);
      request.setRequestHeader("Content-Type", "application/json");
      request.send(requestData);
    }
  }

This is the text from the browser error:

0: Object doesn't support property or method 'setState'

Any ideas why this happens or how to fix?

2

There are 2 answers

2
Daniel On BEST ANSWER

The error is because you are using request.onreadystatechange = function() {} instead of request.onreadystatechange = () => {} or request.onreadystatechange = function() {}.bind(this)

When you use function(), the scope of this changes.

Remember that function() {} and () => {} are not the same.

but function(){}.bind(this) and () => {} are the same.

0
Shubham Khatri On

You need to bind the onreadystatechange function using .bind or arrow function since when the function is executed it needs to access the correct this value referring to the class context.

request.onreadystatechange = function() {
    // all logic here
 }.bind(this)

or

request.onreadystatechange = () => {
   //all logic here

}