Meldon
Meldon

Reputation: 850

React: How to use this.state in subsequent setState() calls? How to force an update to this.state?

I'm working on a small application with which you can keep track of some game scores.

The game is played between two players. A player can score points but can also incur a penalty. Instead of substracting the penalty from the player's score, it gets added to his opponent's score. The opponent should also get the next turn.

I setup a demo that implements the above description: https://codepen.io/anon/pen/ybLQvR

As you can see in the demo it doesn't quite work: when a player incurs a penalty he gets awarded the penalty, instead of his opponent.

This is due to setState within setTurn not being processed immediately. setScore still uses the 'old' state in which turn has not been updated.

I'm aware this can be handled by passing a callback to setState instead of an object (see the commented code), which takes in the previously set state. If I do that things work as expected.

However, I need the updated value of state (in this case this.state.turn) in the addScore outside of the setState call as well. How would I achieve this? I've tried this.forceUpdate() to no avail.

Should I wrap the entire function body of addScore in the setState callback? How would I then return any calculations based on this.state.turn from the addScore method should I need it?

Side question: why doesn't this.forceUpdate() work? Does it just trigger re-rendering the component without processing the state queue?

Thanks for helping me out!

Upvotes: 0

Views: 375

Answers (1)

Callum Linington
Callum Linington

Reputation: 14417

I see what you're saying now. To my undestanding what you are trying to do is make sure that the setState method has been executed before another setState method.

Now the docs have this to say on the matter:

The second parameter is an optional callback function that will be executed once setState is completed and the component is re-rendered. Generally we recommend using componentDidUpdate() for such logic instead.

So by their recommendations you should use componentDidUpdate(). So how is the question now.

componentDidUpdate(prevProps, prevState)

That is the method signature. So we can see here that the previous state gets passed in, and we now have access to the current state via this.state.

See codepen for the answer.

Basically - I turned the addScore and changeTurn into functions that were just snippets of the setState. This means we can combine them together with or state changes to make one atomic operation. And they can be reused without adding complicated default parameters for different scenarios.

Secondly, I added in functions which applied these methods in order to set the state. These are what the buttons use!

Finally in the componentDidUpdate function there is now a chance to apply actions based on the current action that is being executed. Do not add a setState for the default action, otherwise you'll get an infinite loop of updating......

Upvotes: 1

Related Questions