Help me on the asynchronous behaviour of setState

I was trying to build a Tic Tac Toe game as a part of FFC challenge.

But I’m stucked at some points coz of the setState.

This function listen for a click event by the user and set the cell values according to the click and call another function after 1 second to make the bot play.

  const clickHandler = () => {
    if (!isBotTurn && !winner) {
      setValues((values) => [
        ...values.slice(0, index),
        choices.user,
        ...values.slice(index + 1)
      ]);
      if(!winner){
      setIsBotTurn(true);
      setTimeout(botChoice, 1000);
      }
    }
  };

the botChoice function will generate a random index and check whether the cell is already filled or not.but the problem is the values array inside this function is before the update on the clickHandler function .(since setState is async).so it overwriting the existing cell values.(means if I (X) click on first cell and the bot also pick first cell then that cell will be ovewritten by “O”)

const botChoice = () => {
    let random;
    do{
      random = Math.floor(Math.random()*9)
      console.log(values)
    }while(!(typeof(values[random]) == "number"));
    setIsBotTurn(false);
    setValues((values) => [
        ...values.slice(0, random),
        choices.bot,
        ...values.slice(random + 1)
      ])
  };

How to solve this problem.

Here is the link to the whole code codepen

@kevinSmith

There have been a lot of these conversations of late. This is a great place to consider useEffect, as the check-for-win logic and the computer-turn logic are not directly related to the player-turn.

But, when the values changes, you can easily trigger a useEffect, check if isBotTurn, and handle any logic you need for that - and that would only trigger after the asynchronous setValues has happened.

1 Like

But if I add something like

useEffect(botChoice,[values])

it will end up in an infinite loop right? (coz of that setState inside botChoice function)

Have you tried it? It only watches the dependencies for change, so the [values]. Not the botChoice function, I believe.

Best way to know might be to try it and see if it breaks.

I mean the values is being updated inside the botChoice function

  React.useEffect(()=>{
    if(isBotTurn) {
      console.log(isBotTurn)
      botChoice()
      // setTimeout(botChoice, 1000);
    }
  },[isBotTurn])

I added this and its running about 5 times on each click

Yep - because when the bot takes its turn and change the isBotTurn, then that triggers the useEffect again. Considering other approaches. :thinking:

It’s late, I’ll try to take a closer look tomorrow, but I’m a little confused.

I don’t understand this:

but the problem is the values array inside this function is before the update on the clickHandler function .(since setState is async)

It seems to work fine to me.

so it overwriting the existing cell values.

I’m not seeing this. I’ve played 20 games and I haven’t seen it once. The do while loop prevents this, right? Alternatively, you could have just selected randomly from the emptyCells return value, right?

If you’re worried about a race condition, couldn’t you put setIsBotTurn(false); after the other? Or could you use values as your dependency since you are creating a new reference when you set it? (I think.)

I don’t know, I’m tired, I’ll put a bookmark on this and look in the morning.

1 Like

I have just changed the code slightly. added this

  React.useEffect(()=>{
    if(isBotTurn && !!emptyCells() && !winner) {
      var timeout = setTimeout(botChoice, 1000);
    }
    return ()=> clearTimeout(timeout)
  },[isBotTurn,winner])

thats why its not overwriting

Ohh sorry for that its 10 in the morning here :sweat_smile:

OK, then what is the issue that you are still having? Wasn’t that what you wanted to solve? That solution you have seems to be reasonable to me.

1 Like

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.