BunnyHOPE
BunnyHOPE

Reputation: 63

React native: setState did not update immediately

I'm trying to make a TicTacToe game for an android app. The TicTacToe's game mode that I'm intended to make is Human-vs-Computer. But the problem is the program did not update the switching-procedure (setState) in my switchPlayer function as the Previous player and Current player is the same after doing the setState. The expected output from switchPlayer function: Previous player: 1, Current player: -1 but the actual output: Previous player: 1, Current player: 1.

I've also tried to use the callback method as suggested/recommended from other Stackoverflow question, but still not working. Am I doing it right? Any suggestion will be much appreciated. Thank you in advance.

Codes are as below:

switchPlayer = (newPlayer) => {
   console.log("Previous player: "+this.state.currentPlayer);
   this.setState(

   {currentPlayer: newPlayer},
   function () {console.log("Current player: "+this.state.currentPlayer); }

   );
};

onTilePress = (row, col, ValidMove) => {

   if (ValidMove == 1) {  //If move is valid

       //Dont allow tiles to change
       var value = this.state.gameState[row][col];
       if (value !== 0) { return;}

       //Identify and grab current player
       var PlayerNow = this.state.currentPlayer;   

       console.log(row, col, PlayerNow);
       //Set the correct tile...
       var arr = this.state.gameState.slice();
       arr[row][col] = PlayerNow;
       this.setState({gameState: arr});

       //Switch to other player
       if (PlayerNow == 1) { //if player 1, then change to bot
           this.switchPlayer(-1);
       }            
       else if (PlayerNow == -1) {
           this.switchPlayer(1);
       }

       console.log("New current player " + this.state.currentPlayer);

       //check winner
       var winner = this.getWinner();  //get the winner update
       if (winner == 1) {
          Alert.alert("Player 1 has won!");
          this.initializeGame();
       }
       else if (winner == -1){
          Alert.alert("Bot has won!");
          this.initializeGame();
       }
       else if (this.checkTie() == 9){ //check if the match is a draw
          Alert.alert("It's a draw!");
          this.initializeGame();
       }
       //Alert.alert("It's Player "+takePlayer+"'s turn!");
       this.BotMove();
   }
   else {
       Alert.alert("Player 1, please make a move!");
   }

}

Upvotes: 3

Views: 160

Answers (2)

Max Starling
Max Starling

Reputation: 1037

From React doc:

Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.

If you want to handle update you could try componentDidUpdate

componentDidUpdate() is invoked immediately after updating occurs. This method is not called for the initial render.

componentDidUpdate(prevProps, prevState) {
  if (this.state.currentUser !== prevState.currentUser) {
    console.log('previous user:', prevState.currentUser);
    console.log('current user:', this.state.currentUser);
  }
}

If you want to just check that something has been updated (without comparison with the previous value), you can use render

render() {
  console.log('current user', this.state.currentUser);
  /* ... */
}

There was one more method componentWillUpdate(nextProps, nextState) where you could compare current and the next state but it's marked as unsafe now, so you shouldn't use it

componentWillUpdate(nextProps, nextState) {
  if (this.state.currentUser !== nextState.currentUser) {
    console.log('current user:', this.state.currentUser);
    console.log('next user:', nextState.currentUser);
  }
}

https://reactjs.org/docs/react-component.html#unsafe_componentwillupdate

One tip that can solve many problems: don't use function(){} as callbacks, use arrow functions instead () => {}, because function(){} has its own this and arrow functions don't have it

Upvotes: 0

Ruan Mendes
Ruan Mendes

Reputation: 92274

If your problem is that you expected the log statement after the calls to switchPlayer() to already include the state changes... Then you must put everything after your call to switchPlayer in a callback that has been propagated through switchPlayer

switchPlayer = (newPlayer, cb) => {
   console.log("Previous player: "+this.state.currentPlayer);
   this.setState(
       {currentPlayer: newPlayer},
       function () { 
          console.log("Current player: "+this.state.currentPlayer);
          // Callers should put their code 
          if (cb) {
              cb();
          }
       }
   );
};

onTilePress = (row, col, ValidMove) => {
       // Switch to other player
       if (PlayerNow == 1) { //if player 1, then change to bot
           this.switchPlayer(-1, () => {
                // Here, you know the state has been updated
            });
       }            
       else if (PlayerNow == -1) {
             ....
       }
       // This code executes before the state is changed, you get the old state
       // All your code should really be in the callbacks to `switchPlayer`
       // or to any method that calls `setState`
       console.log("New current player " + this.state.currentPlayer);
       ...
}

Upvotes: 1

Related Questions