3

Trying to update the react state within if statement.

When snapshot returns true -> switch state from true to false. When snapshot returns false -> switch state from false to true.

If i console.log(this.state.notification) above the if statement, everything is fine, when i log or want to edit inside the if statement i keep getting error: [Unhandled promise rejection: TypeError: undefined is not an object (evaluating 'this.state.notification')]

i tried to use bind but didn't work.

Help please!

state = {
    darkMode: true,
    notification: true
};


toggleNotification = async () => {

    console.log(this.state.notification);       // no error

    const currentUserId = firebase.auth().currentUser.uid;
    const ref = firebase.database().ref("users/" + currentUserId + "/notification");

    ref.once("value").then(function(snapshot) {
      if(snapshot){
        this.setState({ notification: false })  // error
        console.log(this.state.notification);   // error
        console.log('First check')
      } else {
        this.setState({ notification: true })   // error
        console.log(this.state.notification);   // error
        console.log('Second check')
      }
    });
};
brunelli
  • 315
  • 2
  • 10

1 Answers1

5

There are three issues here (perhaps four): :-)

  1. this is not what you expect in the then callback. You'd solve that using the answers from this question, but see #2:

  2. Don't use then on promises in an async function; use await.

  3. The "Unhandled promise rejection" tells us that nothing is handling errors from your toggleNotification function. Remember that async functions return promises, and that one of the cardinal rules of promises is: Handle errors, or propagate the promise chain to something that will.

#1 above is superceded by #2. Here's how you implement #2 in that function:

toggleNotification = async () => {

    console.log(this.state.notification);

    const currentUserId = firebase.auth().currentUser.uid;
    const ref = firebase.database().ref("users/" + currentUserId + "/notification");

    const snapshot = await ref.once("value"); // <=====
    if(snapshot){
        this.setState({ notification: false });
        console.log(this.state.notification);
        console.log('First check')
    } else {
        this.setState({ notification: true });
        console.log(this.state.notification);
        console.log('Second check')
    }
};

(#3 you do where you call toggleNotification.)


I notice you log this.state.notification after calling setState to update it. Remember that state updates are asynchronous, your state won't be changed yet as of that log call. If you want to log the updated state, use the completion callback (the second argument to setState:

this.setState({notification: false}, () => {
    console.log(this.state);
});

(Of course, if you were intentionally logging the old value of this.state.notification, fair enough, though it might be better to do that before the setState call if that's your intention, as it's easy to misread otherwise.)


If you like, you can avoid the duplicated code in that if/else

const notification = !await ref.once("value");
// Notice -----------^
this.setState({notification}, () => {
    console.log(this.state.notification);
    console.log(notification ? 'Second check' : 'First check')
});
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875