
Lesson Description
The "Combining State Exercise" 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 instructs students to refactor a flight booking form by combining related state variables into a single cohesive state, replacing Boolean flags with finite states, and using string enums or discriminated unions for status handling. He also discusses using arrow functions within handlers and memoization trade-offs.
Transcript from the "Combining State Exercise" Lesson
[00:00:00]
>> David Khourshid: Great. So we are going to jump into the next exercise. And so what we're going to be doing is two things. First, we have a flight booking form. We're going to be combining related state variables. And this is just again to practice getting used to combining related state into one single use state.
[00:00:26]
And also because there is some loading and error and success states, we also want to remove the Boolean flags and create finite states instead. And you could start by just having a simple status as a string enum, or if you want to again go above and beyond, you can use discriminated unions and use those type states.
[00:00:54]
All right, so let's clean this up a little bit with some finite states and combining things. Another thing that I really want to talk about too is the strangler fig pattern. And this is actually going to help us solve this exercise because likely you don't want to just delete code and replace code and then pray that it works.
[00:01:16]
Instead, you really want to check side by side if the code that you're replacing the previous code with is going to be the correct result. So we could combine pretty much all of these, like the destination, departure, arrival and passengers together and we could combine it into one.
[00:01:39]
Let's call this flight data. And actually it's not going to be that. Let's turn off this. So this is going to be the destination, departure, arrival and passengers. And so this is going to be the default states over here. So we have empty string, empty string, empty string and passengers.
[00:02:08]
We'll just default it to 1. And so again, we are going to identify these Boolean variables which are actually mutually exclusive. And we're going to change this and put it into a status. So const. Status, set status. We're going to use dates. And so we could start off with saying that, you know, maybe it's idle.
[00:02:30]
We didn't do anything yet. But we could change this to a string string type or what I like to call a string enum. Or at least it functions like when it's not literally a string enum. So idle, submitting error and success. And so what this allows us to do is we could actually just side by side work on a, you know, work on refactoring this while just maintaining the same structure that we have.
[00:03:08]
So for example, when we set the destination instead of this, we could just, we could set the flight data to again, we're using previous. So we are going to get the previous data and set the destination to E target value. And so we could do this for the other ones too.
[00:03:33]
Just to make sure that everything works. So I'm going to do this for that. And again, this is the exact same thing that we're doing. We're taking the previous data and we're just updating that one field over there and same thing over here. Another quick trick. You see that we're doing parseints over here.
[00:03:58]
What we could actually do is E target value as number and that basically does the exact same thing. So then when we have things like issubmitting, we could do one of two things here. We could either keep the same variable or we could have a more explicit like flight data.
[00:04:25]
We don't have the status. So we could say status issubmitting or like we learned about in our first exercise, and this is actually a really good way to not change too much code but still do the refactor that you want to do. We could keep that issubmitting and instead we could derive that data.
[00:04:48]
So we have our status over here. We could say const isSubmitting is basically if the status is submitting. So these boolean flags become something that's derived rather than something that becomes a source of truth. So again, that first exercise is really showing us how we could improve the way that we model these things.
[00:05:14]
So now if I get rid of this, we see a bunch of variables together over here. And so instead of all of that, we could just set the status to submitting and then get rid of the other ones. And then we'll set the selected flights to null. This is also a good indication that if you want to, you could use type states to really combine that into a single cohesive state variable.
[00:05:42]
So instead of set success true, we could set the status to success. And then over here we could set the status to an error. So now you're going to see that again, lots of red here. We're no longer using this. So we could do things like derive more data.
[00:06:01]
So we could say destination is flightdata.des Station and in fact we could just pull out departure, arrival and passengers from here. And that way we have limited changes to our code, but we can reduce the number of use states that we have. And we could have all related data in a single object.
[00:06:27]
So just going through this again, we no longer need things like set iserror true because now we're setting the status to an error. And so same thing over here, iserrorn is success. You could either change that to checking the actual status or you could derive that data. So we could say const iserror is if status is equal to error or const issuccess is equal to if the status is a success and then everything should work the exact same.
[00:07:01]
So again, being able to look at your application and understand the different flows that it could be in is really going to help you out here. So in this case we know that this can be considered some sort of an idle state and you might choose a date and then we have this loading state over here which we could visually verify.
[00:07:30]
Then we have the state over here where we successfully have data. We could select a flight and we could see that booking summary. And if we wanted to, this can be a different state on its own. That is actually going to be the case when we go into future exercises.
[00:07:48]
So yeah, any questions about this exercise and how we refactored everything
>> Speaker 2: not directly related to the exercise, but there was a question about your opinion on using arrow functions within a handler, because isn't passing the function directly to the handler better approach?
>> David Khourshid: Okay, so yeah, the question was about, for example, handleFlightSelect if you could do it here.
[00:08:21]
So it sort of doesn't matter to me. It is dependent on your team though. So some of your team might like to put all of their handlers up in front and I think that that's a very good way to see everything. Just like that principle we talked about where we're separating the framework from the actual logic.
[00:08:40]
And so by having the handlers up in here, then you see all of the logic together and then all of the UI below that. So it is pretty nice to do that in terms of arrows or functions, that doesn't matter to me either. But yeah, another thing that some people could do is instead of handle Flight select, they might just set selected flights directly over here.
[00:09:08]
And I honestly think that that's fine too. If you find yourself repeating that a lot, that's where a handler might be useful. I think a lot of React developers do go overboard and they try to memoize that by putting it in a use callback. And honestly, if you don't need it, then it doesn't really matter if you do it or not.
[00:09:30]
I like to only memoize if I notice that there's a problem.
>> Speaker 3: Isn't there some kind of performance overhead associated with UseMemo and Use Callback too? So if you're using it when you don't need to, it can actually make things perform worse.
>> David Khourshid: Exactly, yeah. This is something that you learn in computer science.
[00:09:50]
I only took a couple of classes, but when you're doing optimizations such as caching, memoization, et cetera. It's not that you're making everything magically faster. You, you're trading performance for memory, so there are trade offs there. And so if you try to use Memo everywhere, then the internal memory used by the React app will, you know, it might balloon, it might slow things down, honestly, like everything's a micro optimization until you actually run into the issues and see that, okay, this is slow, but if I do this, then it's definitely visually faster for the user, especially on a slow connection or low powered computer.
[00:10:32]
So yeah, I like to be pragmatic about performance basically only when necessary.
>> Speaker 4: I know React19's goal is to release the compiler in its full state and so therefore they're attempting to try and minimize the need for UseMemo and Use Callback. What's your take or opinion on that shift in their paradigm?
[00:10:59]
>> David Khourshid: I think that, yeah, the React compiler is great and this is more reason to use idiomatic React patterns and not try to do anything crazy or try to over memoize or do callbacks or things like that. I will say that in larger code bases too. First of all, with React Compiler there is that expectation that you're using React and React hooks as designed.
[00:11:24]
However, I have seen in many code bases, and it's sort of inevitable with larger code bases that people will have a long, long dependency array with like five or seven different items inside and they will be missing maybe one or two different variables that should be in there, but they'll be missing them on purpose because if you add those variables in, then the useEffect might run multiple times or the useMemo will unnecessarily call itself again and maybe have a side effect in the useMemo, who knows.
[00:11:55]
But the idea is that they will disable the eslint rule for that dependency array because it's one of those it works, don't touch it. And if you complete the dependency array with everything required, then it's going to break. And so there's so much of this in existing React applications and that actually doesn't work well with the React's compiler.
[00:12:17]
So that's why I say try to avoid having too many things in dependency arrays or even using hooks that require the use of dependency arrays. It's going to make your life simpler and it's going to make React compiler work a lot better because you're just doing things the most natural way.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops