Current section: Advanced State Management 36 exercises
solution

Action Function

Loading solution

Transcript

00:00 Again, I typically like to start by updating the use and then we can update the implementation. So the way that we want to use this is we're going to take the current state and use that to return the new state.

00:14 So current state dot count plus the step and do the same thing for our decrement. So here we'll just decrement and this will be minus the step. And with that, now this is the API we want to support.

00:30 To make it work, we're going to have to make it so that the action can be either the partial of the state or a function that accepts state and returns the partial state. So that makes TypeScript happy, but it actually doesn't work. So if we save this, yoink, yoink, yoink, yoink, yoink, and the reason is that we're passing a function.

00:49 So this action is function. We're spreading the function. That isn't quite right. So we need to handle the situation where the action is a function. To do that, we're going to do a little ternary right here. I know some of you are going to throw up in your mouths. Feel free to multiline this thing if you like, but we're going to say type of action is a

01:09 function. If it is, then we're going to call that with the state. Otherwise, we'll just use the action and then we'll spread the results, the completion value of this expression, into the larger object of state that we are creating here.

01:26 And with that all situated, now our counter is working, hooray! And the step still works, yoink. So we're in a good spot here. So we updated our state updater functions here so that they access the current version

01:44 of the state, and then we proceed. So I don't think it's been made totally clear why this is so necessary. So for one, if this is happening asynchronously, here, actually, let's show you the problem first.

02:01 So if we just pass an object like this, it technically does work. However, if we were to, say, set timeout and stick this in there and then do this after 500 milliseconds, now if I click, boom, boom, boom, boom, boom, it only increments by one

02:21 because at the time that increment was created for each one of those clicks, the count was at zero. And so we have a stale version of the count right there. The other thing that this helps us to, and actually, let me just show you that we don't

02:37 have the problem here by doing the same for our decrement, except we'll leave decrement being the callback form. So here we go. So with the callback form, I click decrement three times, one, two, three, and it actually

02:55 does decrement properly because we're always getting whatever the current state is at this time, which will be incremented properly. The other thing that this helps us avoid, if we get rid of this, is let's say we want to call this multiple times. So we'll call it three times for each one of these.

03:14 So you would expect that by decrementing, it's going to go minus three. Boom, that works, hit it three times. But here we're saying set state, count plus step, count plus step, count plus step. That count variable, there's nothing in our code that will allow this count variable to

03:31 be anything other than zero on this initial render, or now it's at minus three. So like, in fact, this is a const declaration, and so we can't reassign it to anything else. So for all of these set state calls, this value is going to be negative three, and that's why when I hit increment, it's setting it to negative two.

03:51 So it's just incrementing it by one, even though we're calling it three times. So for these two reasons, if your state depends on previous versions of the state, then you should definitely be using the callback form. So there you go. That's why it was important that we update our reducer to support that so that we can

04:10 handle those interesting scenarios. I hope that you learned something interesting. Let's move on.