This course still contains valuable patterns in React, but doesn't cover React Hooks introduced in React 1.16.

Check out a free preview of the full Advanced React Patterns course:
The "State Reducer Component Solution" Lesson is part of the full, Advanced React Patterns course featured in this preview video. Here's what you'd learn in this lesson:

Kent codes the solution to separate state from modifying state behavior in the component.

Get Unlimited Access Now

Transcript from the "State Reducer Component Solution" Lesson

>> Kent C. Dodds: All righty, so one of my favorite patterns, right next to renderProps is a state reducer pattern. So let's pop in our test here, testing the exercise version. And the error that we're getting here is actually if you look at the test it's kind of fun. We're just calling toggle a bunch of times.

[00:00:21] I added little comments just to make things a little less confusing. So, at toggle number five, so between each one of these it should be off, it should be on, it should be off, it should be on. And then when I toggle it the fourth time it should be off.

[00:00:38] And then the rest of the times I try to toggle it, it should stay off. And then we do some assertions to make sure things are called properly.
>> Kent C. Dodds: And so where it's failing is, it's toggling at that fifth time, but it should stay off. It's not, it's toggling on.

[00:00:56] So that is what we're going to solve here with our internal set state function. This is going to accept our updates or you could call it changes, that's what I called it, callback. So the stimulate is the same kind of API that set state has. So all of the times that you're calling set state, you should now call Internal Set State, and it will take care of Client Set State under the hood.

[00:01:29] So, that's what this koala bear was doing. I should have put them right there and then right there, but it is what it is, sorry, Cody. Okay, so with that, we're going to call this Set State. And we're get our state as an argument to our updater function then we'll pass a call back as the second parameter.

[00:01:48] So we can call onToggle and on reset when that states happened. Okay, so now that we have the current state here. Maybe, I'll just differentiate it by calling it current state. We need to get what the actual changes are, because in some cases, the changes will just be an object.

[00:02:08] In other cases, the changes could be a function. So we need to get what those changes are. So I'll have, let's see, what did I call it? The, I don't know, actual changes, or the changes object. So I called it, and they would say type of changes, it's a function.

[00:02:28] Then I'll call it with the current state otherwise we'll just use changes. Okay cool, and let's give myself a little more room on the editors side. Okay, so now we have the changes object and that's what we're gonna be calling the state reducer with. So, I will say, reducedChanges = this.props.stateReducer, with the currentState and the changesObject.

>> Kent C. Dodds: And its responsibility is to take that changes object and return me a new one. And that's what I'm gonna return from my updater, reduced changes.
>> Kent C. Dodds: And that's it, that's the simple case. There are a couple other things we need to consider but that's the basic idea of a state reducer is it internally, inside the component, you replace all usages of set state with internal set state.

[00:03:36] And that uses an update or function to get what the changes should be and then replaces or returns the reduced version of those changes. Now, the reducer could just return the changes objects that you give it. That's totally cool if it does that. Or it could totally change it totally change it to something entirely different.

[00:03:58] But whatever it gives us back is what we're gonna be returning.
>> Kent C. Dodds: So there are a couple other things that I wanna talk about here but. And I'll go ahead and do this really quick actually. So let's say that the state reducer will return null. Or, sorry, it returns an empty object, in some cases.

[00:04:27] So return empty object. So in that case, there are no updates, and so a rerender is not necessary. Because no state is going to be changed. And so up here, we can say object.keys, reduce changes.length. So if it has keys, then we'll return the reducechanges, otherwise we'll return null and that will prevent or unnecessary re render.

[00:04:56] In addition to that, they could also return null from their state reducer to indicate that hey. I don't want any changes and we want them to be able to do either and it's fine. You want to return an object, that's whatever. So we'll just add or an empty object so it doesn't blow up when we do object keys.

[00:05:14] So if they return no, we'll default that to an empty object.
>> Kent C. Dodds: Okay, what questions do you have about state reducers?
>> Kent C. Dodds: Yeah?
>> off screen male: Props on using super.setstate and just making this dot set state transparent to the class? Or would you rather just be explicit?
>> Kent C. Dodds: I see what you mean, so like having set state.

[00:05:42] Yeah, yeah, and then super? Yeah, that's interesting, that would be fine with me. I think that if I were to come into a code base and see we're just set state. And then I realize that's actually not set state as I'd imagined, that might be confusing to me.

[00:06:06] But I'm totally cool with that, if you want to do it that way. Then these would be setState, and that'd be kinda nice. Like you said, transparent like everywhere else in the code. So yeah, I'm pretty sure that will pass the test, feel free to do that if you want.

[00:06:25] Other questions, or comments, observations?
>> Kent C. Dodds: Okay, so one other thing, I was going to do this but then I had some people review my stuff before the workshop and they felt this was a little bit confusing. But I actually kind of like this. One thing that I did was I'd say return this array of changes, and then I'd call map on it over and over again and pull out the first one.

[00:06:55] And what this allows me to do is rather than creating a bunch of variables, I can do this. See that. And then do this one, so that's the new changes, and each one plays into the next and we do this
>> Kent C. Dodds: And then we get rid of all this stuff.

>> Kent C. Dodds: So if you're looking at that and you're also confused, feel free to continue using the variables. But what I like about this is that I can easily add more operations to the changes object without having to create more variables. So what does this effectively doing as we turn this into an array only for the purpose of being able to come up on it.

[00:07:55] And it's gonna map the whole item and we'll pull the last or the first item off, which is gonna be our one item. So we say, here our changes, if its a function, then we'll call it and we'll turn the changes. And here are the changes we're gonna reduce it and we'll return the changes, here the changes.

[00:08:11] We'll check its length then we'll return the changes. And then, so you have to do it all the way. I kinda think it's look kinda clean but if it's confusing to you all, then that's why I in the solutions, that you will be working with these stuff, these is the very rules.

[00:08:26] But I just wanna show this to you, if you wanna do this with, yeah, question.
>> Kent C. Dodds: Okay, is there something wrong with this implementation?
>> Kent C. Dodds: Yeah, okay so, here maybe I'll copy it over and test pass. So I'm gonna change setState to internal or setState so I don't confuse myself.

>> Kent C. Dodds: Okay, so their solution, comment that some more aas something like this. And we'll change this to callback and just updater oops. Okay, so, there are couple of problems with this implementation. So as we were talking about before, you need to use. If you want your set state operation to reference preexisting or the current state, and to avoid issues with set state batching, then you don't want to reference this.state.

[00:09:46] You'll use an updater function. So that's the first thing that I would say to avoid here is, make sure that all of. The code that's referencing the current state lives within an updated function, to avoid inconsistencies with a state. Other than that this essentially looks pretty similar to what we had here, and, so, yeah, other than that it's pretty much the same.

>> Kent C. Dodds: Here, the updates the user or the state reducer, whoever is using the state reducer could actually return an empty object or null. And so that's why we're doing this object.keys thing. But you could enforce in your API and say, hey listen, if you don't want a re-render then return null If you return an object then that's just what I'm gonna return.

[00:10:36] I just added the this little line for convenience. So okay so taking this back to what the solution is that you'll be looking at. Looks something like this.
>> Kent C. Dodds: Okay, what other questions do y'all have?