2

I'm working on a matching game and have written a handleClick function that should increment a state value called guessCount so I can keep track of each time a player guesses. I also want to save the IDs of each item clicked as guessOne and guessTwo in state. For some reason the first click won't trigger my setState guessCount increment code nor capture the clicked item's IDs. Each click after does work correctly, except that it's one click behind the user's actual clicks.

state = {
  guessCount: 0,
  guessOne: null,
  guessTwo: null
}

handleClick = (e) => {
  this.setState({ guessCount: this.state.guessCount + 1 });

  if(this.state.guessCount === 1) {
    this.setState({ guessOne: e.currentTarget.dataset.id });
    this.setState({ guessCount: this.state.guessCount + 1 });
  }
  if(this.state.guessCount === 2) {
    this.setState({ guessTwo: e.currentTarget.dataset.id });
    this.setState({ guessCount: 0 });
  }
}

Expected results: On first click, the guessCount would increment from 0 to 1 and the clicked item's ID would be set in state to guessOne. On second click the guessCount would increment from 1 to 2, the clicked item's ID would be set in state to guessTwo, then the guessCount would be reset to 0.

2 Answers2

1

Since react state updates are asynchronous,they are not updated immediately. There is no guarantee that after you call

this.setState({ guessCount: this.state.guessCount + 1 });

"guessCount" will set to 1 immediately.Hence while it reaches the condition "guessCount" may remain zero.

React Docs : https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous

You could either put your conditional checks inside a function and pass that function as a callback which will get called once state is updated.

handleClick = (e) => {
    this.setState({ guessCount: this.state.guessCount + 1 },()=>{
         countUpdaterFunction();
    });
}
0

setState is asynchronous, you should use an updater function to get the state value at the time the change is being applied.

handleClick = (e) => {
   this.setState(state => ({ guessCount: state.guessCount + 1 }));

   if (this.state.guessCount === 1) {
     this.setState({ guessOne: e.currentTarget.dataset.id });
     this.setState(state => ({ guessCount: state.guessCount + 1 }));
   }

   if(this.state.guessCount === 2) {
      this.setState({ guessTwo: e.currentTarget.dataset.id });
      this.setState({ guessCount: 0 });
   }
}

By the way I think you can have some problem with your code, imagine the function handleClick is called twice without giving the time to setState to update the component state, I would prefer the following solution where I store the key guessOne or guessTwo in the component state

constructor() {
   this.state = {
      guessKey: 'guessOne'
   };
}

handleClick = (e) => {
   this.setState(state => ({
      [state.guessKey]: e.currentTarget.dataset.id,
      guessKey: state.guessKey === 'guessOne' ? 'guessTwo' : 'guessOne'
   }));
}
Olivier Boissé
  • 13,005
  • 5
  • 32
  • 49