Check out a free preview of the full State Management in Pure React, v2 course:
The "Refactoring to a Custom Reducer" Lesson is part of the full, State Management in Pure React, v2 course featured in this preview video. Here's what you'd learn in this lesson:

Steve argues that reducers are easier to test and easy JavaScript functions, and demonstrates how to refactor the code to mainly use reducers.

Get Unlimited Access Now

Transcript from the "Refactoring to a Custom Reducer" Lesson

[00:00:00]
>> So what we're gonna do is we're gonna create a reducer for ourselves. We'll say fetchReducer. And we know that that takes state and an action. And one thing I will probably give myself for when we use the useReducer is some kind of initial state. I could do it in line.

[00:00:18] I'm gonna make it explicit. I'm thinking you could probably break this out into other files. But one thing I've noticed from doing this with groups is me switching files a lot, makes no one happy. So I'm gonna keep it all in here, but if there's a part of you going, that should be its own file, listen to that part of yourself.

[00:00:32] It's right, just not for teaching. So we'll say that result, the same stuff we had here, result is null. Loading is true, and error is null. Right, so this is the kind of various states that things can be in. And I wanna make sure that if no action hits that we just don't change the state of the world.

[00:01:00] That means we won't return undefined. Some people will say, okay, if we get here, throw an error. That's reasonable, too. All right, so if the action type is, FETCHING, well, then let's call it LOADING. I don't like FETCHING. We'll say that the result, it's really this initial state, right?

[00:01:32] So I'll just copy that. I could even reference it, but, If the action type, Is RESPONSE_COMPLETE, Well, then that is going to be, That loading is no longer true. And that the result is the action.payload.response. And finally, if there was an error, And what I like about this is you could see if there were more states in your useFetch, you'd have to start adding more useStates for everything.

[00:02:21] So this is kind of like we're talking about Ajax. But we're also talking about situations where you're changing multiple pieces of state. Hypothetically, imagine a form that, email and password, cool. There's only two pieces of state. First name, last name, address line one, city, country, zip, so on and so forth.

[00:02:42] It becomes a lot of state. And anytime you get to that more complicated stuff, it might make sense to use a reducer. So the action type is ERROR. Then return that, we are no longer loading. I'll say it's action.payload.error, right? So now we have basically the three states, either we're fetching, things went well, things went poorly, right?

[00:03:10] And we have the rules that should happen in each case. So this changes a little bit now. We don't need to manage all of these little pieces of state at once cuz we have one kinda state management paradigm that deals with all of that. So we'll say state and dispatch = React.useReducer, and that's gonna be reducer.

[00:03:33] Nope, yep, fetchReducer is right, and initialState. So now we just basically announced what's going on at any given moment, right? So we'll say dispatch of the type, LOADING, and you noticed before we had a function called addGrudge that dispatched all the information. I could break this out into functions as well.

[00:03:58] But I can just do it like this. I'm mostly doing it like this now to show you that, yes, we can do that. We're gonna say dispatch of the type, where we call it RESPONSE_COMPLETE. The nice part if you make those constants I made in the previous example, you get autocomplete, too.

[00:04:19] I'll say that the payload is the response object. And if things go poorly, We will dispatch of the type ERROR, and the payload will be that error. Now the nice part is since the reducer is handling turning off loading and not loading, we can get rid of that finally.

[00:04:45] And if we went back to that promise API from before, it's the same basic idea. We're just saying, we are either loading, we've gotten a response, or we had an error. And if we got a response or an error, here's the information about that, right? Let the reducer handle it in this case.

[00:05:03] I call response data. All right. And now, We'll send state.response, and we'll just state.loading, state.error, I could just structure them off an object as well, that'll work. All right, so we're still sending the response. We're sending the loading. We're sending the error. And now we have this better way.

[00:05:33] So whenever your state management gets complicated, useReducer is there for you. This is a case with Ajax, but anything where there's multiple moving parts, you just wanna announce that things happened and separate that out. And again, this has a fetch in it, so it'll be a little bit harder to test.

[00:05:47] But the actual reducer itself is just a JavaScript function, passing all the different events, right, with all the data. Super easy to test because you don't have to mock out Ajax and you don't have to mount a component. You simply say you call the function with the state and the action, you expect that the result is what it is.

[00:06:13] So let's see. All right, it is not loading, so let's find out where my issue is. So first thing we'll see, all right, is my, And what are the actions coming through? So we can see a LOADING and a RESPONSE_COMPLETE came through, that's good. We got the action.payload.response.

[00:06:43] All right, here's the issue. Here I'm calling it result, and here I called it response. And now it loads, right? So use state, refer to it immediately. If things get complicated, don't hesitate to use reducer, right? I would argue for more complicated state management, it is there for you.