State Management at Scale in React & Next.js

useReducer for Complex State Logic

State Management at Scale in React & Next.js

Lesson Description

The "useReducer for Complex State Logic" Lesson is part of the full, State Management at Scale in React & Next.js course featured in this preview video. Here's what you'd learn in this lesson:

David introduces useReducer as a useful hook for managing complex app logic requirements. He explains how combining UseReducer with React context allows for sharing state and dispatch functions between multiple components, similar to creating a global source of truth like Redux.

Preview
Close

Transcript from the "useReducer for Complex State Logic" Lesson

[00:00:00]
>> David Khourshid: Okay, so let's get to the next section. And this is an image that sort of introduces what the next section is going to be about. And it's of course, useReducer. And useReducer is actually a very useful hook when you need it. I know that a lot of you might have come from Redux or come from similar areas where you've either used a Redux reducer or you've used used Reducer, and it just feels like a lot of boilerplates.

[00:00:37]
But the fact is, when you have complex app logic requirements, using a reducer is going to be a lot better than trying to manage multiple use dates, use effects, and even use context. And, and the reason is because useReducer, it's in the name, it literally reduces all of that data and states down into a single function, which you could independently test.

[00:01:03]
So again, for complex logic, useReducer is great for simple states, you could continue to use states and things like that. So let's go to the reducer readme just to talk about it a little bit. What we're going to be doing in this exercise is we're going to be combining two things.

[00:01:30]
We're going to be combining React Context and UseReducer. Because one of the other great things about UseReducer, this is all because you're able to centralize that logic is, is that you could share that between multiple components and sort of make your own Redux, you could make your own global source of truth where you could read the state and also dispatch events or actions to update that state as well.

[00:01:59]
And so this allows you to avoid doing multi level prop drilling or to have this parent component that has a use date, potentially just causing a lot of re renders. And so instead of this, which again, I've seen in so many applications where we're just passing props down through many levels.

[00:02:20]
We could instead use context and use a reducer with context to pass the state and the dispatch function down to whatever component needs it. So we're going to be doing that and we're also going to be combining another thing that we talked about, which is finite states. So we're going to be working on a booking flow, or actually it's a flight picker sort of flow, where the first thing we're going to be doing, like we did in previous exercises, is we're going to be simplifying all of these Boolean state values into either a discriminated union or a single object, depending on how much type safety you want.

[00:03:06]
And Then we're going to be using the useReducer hook for managing these complex state requirements and we're going to be putting it into context so that we could share that. So just to give you an idea of the motivation, I actually have a demo page over here. Actually.

[00:03:27]
No, I don't. I thought I did. Let's find it. Yeah, there we go. Okay, let me see if this is actually in the right place. So we are going to be inside the reducer exercise and let me just move this over. I can't. Okay, that's fine. So I'm gonna copy this, do it the manual way.

[00:03:51]
We're gonna make a demo/page.tsx, just to show you a very, very simplified version of what we're going to be doing. So let's say that you have a multi step form. Now in this case there's only two or three steps, but still it counts. You could scale this up to be however many steps.

[00:04:17]
Reducer demo. Okay, so it's very simple. Just we have one page for searching for stuff and then we have a loading screen and then we have our results screen. And it looks ugly on purpose right now. But the idea, and first let's talk about the flow. What's going to happen is that you're going to be able to search for stuff.

[00:04:42]
So presumably you have a form where you could put in search inputs, you press search and then a loading screen shows up and then after a while you, you get the results. So now you're going to be on the results screen and then you can also go back so you could search for stuff again.

[00:04:59]
So it's a very simplified flow, and in fact let's just go ahead over here and map this out just so you could see what kind of flow we're going to be doing. One minute we're going to be creating a new machine and so first we're going to be in this search form and then when we start to search, not really the most appropriately named so call this search form.

[00:05:37]
Then we're going to be in some sort of loading screen and then once we get the results, then we have a results page over here. And so in the results page we could actually go back to the search form. And so again I'm demonstrating how useful it is to think in terms of, you know, just diagramming things and modeling your app logic, because even looking at this, we're not gonna build this in this quick demo, but I could see that we're missing Something like error.

[00:06:09]
And so we have a failed to fetch results screen. And then even if we add that, I can see that we have another issue in that you can't really retry from here. So we might want something to retry or we might want to do a new search from here.

[00:06:32]
So that's just an idea of the kind of flow. We're going to only be working with this small flow over here. And that's the demo that we're going to be working from. This is where a reducer becomes really handy, not just because we have more complex app logic requirements, but also because we want to be able to share the state and a way to dispatch events to change the state with each of those three components.

[00:07:01]
So the way we're going to do this is by first making a reducer. And so it's going to look like this function, we'll just call this flow reducer. And we're gonna have a state and an action and we'll fill the types in later. But typically when you create a reducer, you switch based on the types first.

[00:07:22]
In previous courses where I talked about XState and building your own state machine, you would switch on the state first and that's a more maintainable pattern. But for this simple example, we're just going to switch on the different action types first. So when we search, you know, we're going to be in a loading state and then once we get results then we're going to be in a result state.

[00:07:47]
And then once we go back, we go back to the search states. So now let's add our types. So we are going to have a flow state and this for now it's just going to be, you know, it's one of these three statuses, search, loading or results. And we're also going to have results maybe so any.

[00:08:15]
Or we might not have results at all. And then we could just add this over here and then we're going to have some actions that we could do that help us transition between the states. So type flow, action, we have search, let's call this receipt results just to disambiguate.

[00:08:40]
And then so we have oops, we have type received results and presumably we'll have some results here too. And we'll, we'll just make this a string and we also have the back action. See, this assumes that we're in the result state. So that's why you might want to add something like if state is search, you can't really go back from there.

[00:09:12]
So this assumes that state is results or maybe it's loading and you could still go back. Remains to be seen. So this is our reducer and we're going to add the flow action over there and then we're going to add that reducer. So const state dispatch, you pass in the reducer over here and then you put in the initial state.

[00:09:41]
Now we're going to have an issue where it's like the types of status are incompatible. So let's see how we could fix that. We have flow state. We should specifically say that it is going to return the flow state. So now everything is as expected. Okay, so in our different views here, we have a button for searching, we have a button for going back from the results view and the loading view, which doesn't really concern itself too much with any of the states because we're going to.

[00:10:17]
It's just an intermediate state that doesn't really show any results or anything, but it could. So how do we actually pass in a way to get our dispatch in here and get the results in the result view? Well, this is where we could use React's context. So I'm gonna say const flow context equals create context.

[00:10:42]
And since we want to share both the state and a way to dispatch it, I'm just going to put it in an object. So this is going to be state, which is the flow state and dispatch. Now in some React documentation they do actually have special types for the dispatch function, but I'm not going to use that.

[00:11:03]
I like keeping things simple. So we're going to say action, flow, action and that's going to be void because this is a function that shouldn't really do anything. I'm going to grab this import from React and then this is always a bit strange, like this is why cursor helps, but typically I would do null as any.

[00:11:24]
The idea is that if you omit the context, then it uses this as the default context. But the idea is you always want to have a provider anyway, so this is unnecessary. But just to make React happy, we're going to either put something simple in here or we could do, I don't know, null as unknown as whatever that type is.

[00:11:49]
That's just one of the weird parts about React. But yeah, there you go. Okay, so we have our flow context. So now when we go to the app, we could actually have a provider in which we give the state and the dispatch function. And now this is going to be available to any component that uses that context.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now