简体   繁体   中英

Cannot read state when using async callbacks function in React.js

So I copied this function from a post here at stackoverflow, my goal was to be able to update a database and profile info right after the signup and before moving to a different route, the results are as I expect working however I can no longer access the state in the callback, can please someone assist me debug this code:

  signup = (e) => {
    this.setState({ loading: true });
    e.preventDefault();
    fire.auth().createUserWithEmailAndPassword(this.state.email, this.state.password)
      .then((userCredentials) => {
        if (userCredentials.user) {
          userCredentials.user.updateProfile({
            displayName: this.state.username,
            photoURL: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQdVmHPt9SajdsWlAZWzqRXkd-SzmmO9bBUV45fLnw5giijsty3OA',
          }).then((s) => {
            fire.database()
              .ref(`master/${userCredentials.user.displayName}/setup/`)
              .update({
                bio: 'Be original',
              })
              .then(() => {
                window.location.reload();
              });
          })
        }
      })
      .catch(function (error) {
        console.log(error);
        this.setState({ signupError: true, loading: false, });
      });
  }

The error happen in catch(error)

TypeError: Cannot read property 'setState' of undefined

I suspect the issue comes from using multiple .then

JS functions has its own this context.

In your case you use this context inside a callback. this context of the react component will be lost inside the callback. That is why catch block is unable to identify the setState method

To preserve and use this context inside the callback, assign this to a local variable (named the variable as self in below example) and access it inside the callback .

  signup = (e) => {
        var self= this;
        this.setState({ loading: true });
        e.preventDefault();
        fire.auth().createUserWithEmailAndPassword(this.state.email, this.state.password)
          .then((userCredentials) => {
            if (userCredentials.user) {
              userCredentials.user.updateProfile({
                displayName: this.state.username,
                photoURL: 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQdVmHPt9SajdsWlAZWzqRXkd-SzmmO9bBUV45fLnw5giijsty3OA',
              }).then((s) => {
                fire.database()
                  .ref(`master/${userCredentials.user.displayName}/setup/`)
                  .update({
                    bio: 'Be original',
                  })
                  .then(() => {
                    window.location.reload();
                  });
              })
            }
          })
          .catch(function (error) {
            console.log(error);
            self.setState({ signupError: true, loading: false, });
          });
      }

You can also use arrow functions , arrow functions doesnot have its own this context, so you'll be able to access this context of the outerscope

.catch((error)=> {
   console.log(error);
   this.setState({ signupError: true, loading: false, });
});

this in catch is not your class, see different between normal function and arrow function. There're two way to solve your problem.

  • First: using arrow function
.catch(function (error) {
  console.log(error);
  this.setState({ signupError: true, loading: false, });
});
  • Second: set local self variable for this
signup = (e) => {
  this.setState({ loading: true });
  e.preventDefault();
  const self = this; // It can be what ever you want, `self` is the most popular
  ...
}

then

.catch(function (error) {
  console.log(error);
  self.setState({ signupError: true, loading: false, });
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM