0

I made a simple rock-paper-scissors game. During onclick I call a function to add a number and change the state. Within that function I then call 2 more functions, one picks a random number between 1 and 3 and converts it to rock/paper/scissors and assigns it to the state, then a another function figures out who won:

addToInput = val =>{
    this.setState({ 
        input:val
    });

    this.picknum();
    this.pickwin();       
}

https://codepen.io/russiandobby/pen/xeRYzw?editors=0002

At first I thought that picknum and pickwin would get executed at the same time, however even if I try to call pickwin within picknum it still won't work.

What seems to happen is that when pickwin executes it looks at an old state instead of the state that gets changed by picknum.

How can I fix this problem?

Eldelshell
  • 6,683
  • 7
  • 44
  • 63

3 Answers3

0

setState is asynchronous (or at least, it can be), so there's no guarantee that the state has been updated when you get to the next lines of code If you need something to run after the state has been set, either put it in the componentDidUpdate lifecycle hook, or pass a callback into setState as the second parameter.

For example, you could skip doing pickWin in your addToInput function, and instead have a componentDidUpdate function like this one:

componentDidUpdate(prevProps, prevState) {
  this.pickWin();
}

That said, rather than setting state, then waiting for it to update, then setting state again i'd recommend you refactor your code to just call setState once. If you're calling it multiple times, you have the potential to cause multiple renders, but it looks like these are all intended to be part of the same operation. Perhaps update your pickNum and pickWin functions to return a value, rather than setting state itself, then call setState once with the results

picknum () {
  let aipick = ["rock","paper","scisors"];   
  let temp = Math.floor(Math.random() * 3) + 0  ;
  return aipick[temp]
}

pickWin(userChoice, aiChoice) {
  if (userChoice === 'rock' && aiChoice == 'scisors') {
    return 'USER WINS';
  }
  // etc
}

addToInput = val => {
  let aiChoice = pickNum();
  let winner = pickWin(val, aiNum);
  this.setState({
    aiChoice: aiChoice,
    whowon: winner,
    input: val,
  })
}
Nicholas Tower
  • 72,740
  • 7
  • 86
  • 98
0

Ok so seems like callbacks can be one of the solutions so what i tried doing is this

  picknum (){

     let aipick=["rock","paper","scisors"];   
      let temp =Math.floor(Math.random() * 3) + 0  ;
  this.setState({
    aichoice:aipick[temp]
  },function(){
   this.pickwin();
 }
               )


}




  ////////////
addToInput = val =>{
   // console.log('add function');

 this.setState({ 

    input:val


  },function(){
   this.picknum();
 }

              );


 } 

seems to work now.

-1

It is a asynchronous mechanism of javascript

 this.setState({ 

    input:val


  });

if you want to be synchronized. Let use async/await. here is my code:

 addToInput = async val =>{
   // console.log('add function');

 await this.setState({ 

    input:val


  });
   await this.picknum();
  await this.pickwin();

 } 

Let try it!

Cuong DaoVan
  • 262
  • 2
  • 6
  • It's true that setState is (or can be) asynchronous, but it does not return a promise so await will not help. – Nicholas Tower Apr 10 '19 at 03:10
  • Seems to have fixed the problem unless i am missing something – russiandobby Apr 10 '19 at 03:17
  • Awaiting a non-promise will only delay the code until the next microtask and does not guarantee that it will wait for the thing you think you're waiting for. If adding await fixed the issue, then you got lucky. This is not the right way to fix this kind of issue. – Nicholas Tower Apr 10 '19 at 03:18
  • then how would one go around fixing this kind of problem. i guess if showed everything into one function and only used one setstate it would fix it but is there any other way to fix it – russiandobby Apr 10 '19 at 03:22