Refactor Checkout.js
The checkout page uses multiple API calls to correctly display the items a product manager wants to ship to stores.
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo set up the project on your local machine, please follow the directions provided in the README.md
file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.
This lesson preview is part of the The newline Guide to Modernizing an Enterprise React App course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to The newline Guide to Modernizing an Enterprise React App, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

[00:00 - 00:18] As we progress through this module's lesson, each component we touch takes the refactoring complexity up a notch, and the checkout component is no different. Whereas the app component had just one lifecycle method and API call, this component has multiple API calls being triggered in many different ways.
[00:19 - 00:29] We'll need to account for all of them. We will learn how to handle converting multiple API calls and functions in this lesson while we convert checkout.js.
[00:30 - 00:44] But we can handle this. We'll follow the recipe and knock it out, no sweat. In what should be becoming a familiar refrain to you, we'll start by converting this class-based checkout component into a function-based component.
[00:45 - 01:03] The class declaration on line 14 of the checkout.js file, which is located on the file path of client source containers checkout, is the first thing that we 're going to focus on. We're going to change this class component into a constant.
[01:04 - 01:16] And then we're going to go down to where our JSX is rendered and delete the render method and the object destructuring right before the JSX rendering. They're both unnecessary now.
[01:17 - 01:30] Don't forget to delete the extra curly brace that's now hanging out there at the bottom. And it's now also safe to delete the react and component imports from the top of the file.
[01:31 - 01:42] They're actually going to be replaced with use effect and use state. So go ahead and delete them and insert, use effect, and then use state hooks instead.
[01:43 - 01:49] This is what we're going to end up using. Okay, our next move is to update the state for this component.
[01:50 - 02:05] And since checkout has both state and props, we'll be replacing the state with use state variables and destructuring the props in this section as well. So what we're going to do is create some new state variables.
[02:06 - 02:26] We're going to have checkout items and set checkout items, which will be an empty array. We will have loading and set loading, which will start out as a state of true.
[02:27 - 02:45] And finally, we will have error and set error, which will start out in a state of false. Okay, so now we can remove our constructor and our props up here, because now our state hooks will take place of that.
[02:46 - 03:00] And we can also update this props.update checkout count. We can remove the this.props and bring in this checkout count as a destructured prop at the beginning, just like that.
[03:01 - 03:06] Nice. So now it's time to start updating the lifecycle methods in this component.
[03:07 - 03:16] But let me touch on one thing before we get started with that. Throughout this course, you're going to see me destructuring nested objects the very same way that I did update checkout count here.
[03:17 - 03:31] I do this not only because it's an ES lint error if I don't, but also because I think it makes the code cleaner and easier to read. If however, you prefer to import all of your props and then access the values inside of the component, that's totally fine too.
[03:32 - 03:42] You could absolutely do something like this. And then wherever you're using those props, you would just do props.update checkout items, totally valid.
[03:43 - 03:56] However, I am going to refrain from that. The next thing that we're going to do is the checkout component has a component did mount function to fetch any products in the checkout when it first mounts in the DOM via the checkout API.
[03:57 - 04:07] So our component did mount method is going to get replaced with a use effect that will look something like this. We will declare a checkout.
[04:08 - 04:20] So we'll create a use effect just as we have in previous instances, put in our callback function. And we're going to declare a fetch checkout items function inside of here.
[04:21 - 04:34] This will actually make the asynchronous call. And then we are going to take a new const that we'll call all checkout items.
[04:35 - 04:41] And borrow this await from up here. Paste it back in right there.
[04:42 - 04:49] And then we can also borrow this if statement. And its getters and setters will update those shortly.
[04:50 - 05:06] So instead of having to do this dot set state of checkout items loading and error, we will replace this and it will just be set checkout items. And we will give it all checkout items, which I misspelled.
[05:07 - 05:19] So let's fix that first. And then if our checkout items call fails, we will just set an error of true and update nothing else.
[05:20 - 05:32] And then finally at the very end, regardless of what happens, we're going to set loading to false so that we stop seeing our loader indicator. And now we will actually call fetch checkout items.
[05:33 - 05:44] And since this is only happening on page load, we will give it a dependency array of empty array. And now you can delete your component did mount.
[05:45 - 05:49] Very good. Okay, we're ready to move on to the other function in this component.
[05:50 - 06:04] The other function that we're going to tackle is this remove item from checkout . This function in the component serves to update the items that are displayed in the cart and trigger the use effect that's back in our app.js file that updates the count of items shown in the nav bar.
[06:05 - 06:15] So when a user clicks the remove product from checkout button on an item while they're in the checkout, this is what fires off. So this is what our current function looks like.
[06:16 - 06:22] Pretty big, pretty long. And this function doesn't really require all that much in terms of updating.
[06:23 - 06:39] We are going to change it to an inline arrow function, so we'll add a const to it. And we are going to change the setters from this dot set state to set checkout items, which we will pass the remaining checkout items to.
[06:40 - 06:50] And we will update that, delete it. And same thing down here, if it fails, we will just set our error once again to true.
[06:51 - 06:58] And we will set loading to false at all times. Save that.
[06:59 - 07:05] Thanks, Prittier, for reformatting. Okay, so I think that we're ready to retest this app's functionality.
[07:06 - 07:16] So let's test the checkout still works in the browser. Go ahead and restart your application if it's not been running. Mine already is. So we're going to switch right on over.
[07:17 - 07:26] Now that we're in the browser, head on over to the My Products page and add a couple of products to the checkout. Looks like those are all adding successfully.
[07:27 - 07:35] And now if we head over to the checkout, we see our products are displayed here . Now we can remove a couple of those products.
[07:36 - 07:41] Very good. And we've confirmed that they have disappeared and the checkout is empty.
[07:42 - 07:44] The empty checkout message is present. This is looking good.
[07:45 - 07:53] So we're good to move on and put the finishing touches on this component. So let's head back over into our IDE and check out if we have any ES-Lint errors to fix.
[07:54 - 07:59] And where would we be without a few ES-Lint errors? Oh, so there is an interesting one here.
[08:00 - 08:05] It looks like I forgot to add an ampersand. So that's one.
[08:06 - 08:12] And then the rest are pretty standard. There's actually two ES-Lint errors that haven't been fixed.
[08:13 - 08:21] And let's tackle the easiest one first. We have this one that says React Hook Use Effect has a missing dependency of checkout items.
[08:22 - 08:36] Well, this was actually an oversight on my part. Because I copy pasted this from our original component, didMount, I neglected to update this to say this should be actually be all checkout items.
[08:37 - 08:47] The Linter is doing exactly what it's supposed to be doing. But I forgot to update this because it's an internally scoped variable, not thing that we want to depend on it changing for the component to update.
[08:48 - 08:50] So that fixes that error. Easy to do.
[08:51 - 08:54] Thank you, Linter, for catching it. That would have been an issue probably later on.
[08:55 - 09:08] But now we are down to our last error, which is the update checkout count is missing in props validation. Not to worry though, this is a pretty standard error and it is our prop that is actually the cause of this error.
[09:09 - 09:19] So let's go to the browser and let's look at what this means in the ES-Lint world. So this ES-Lint error is a fairly common props type error.
[09:20 - 09:28] It's a good linting error to have anti-heat. Defining types for component props improves reusability of your components by validating the received data.
[09:29 - 09:39] It's kind of like TypeScript but TypeScript much lighter. This kind of thing can warn other developers if they make a mistake while re using a component with an improper data type as well.
[09:40 - 09:54] So to fix this error, we're going to need to add a new library to our project, which is called prop types. Let's head back into our IDE and we're going to run the following import in the import inside of our client folder.
[09:55 - 10:29] Go ahead and stop your server and then we are going to do Yarn Add prop types and install. Once it's been installed, we are going to import prop types up at the top of our file, import prop types and prop types and then at the bottom of our component, after the export, we are going to add the following lines to cover our update checkout count prop that's passed in from our parent component.
[10:30 - 10:49] We will do checkout.prop types and then update checkout count. Prop types.funk.isrequired because this is a function that is coming from our parent and it is required, we do not have a default for it.
[10:50 - 11:01] And at this point, that props type error has disappeared from our problems tab. So one thing that I want to say is that prop types are not type script, but they do provide some built-in type checking.
[11:02 - 11:12] Prop types are not as strict as using something like TypeScript for our app. They exist to document the intended types of properties that are passed to components and to warn developers if they don't match.
[11:13 - 11:19] But if they don't match, the app will still compile, it will still run. It is not like TypeScript in that regard.
[11:20 - 11:31] It's a good extra gut check to have during development. They also used to be included as part of the React package itself, but have been broken out since React version 15.5, just as an FYI.
[11:32 - 11:43] And prop types can get pretty complicated depending on what data shapes are being supplied by the parent components. If you want to learn more about prop types, I encourage you to check out the React documentation that I've linked to in the lesson.
[11:44 - 11:50] Okay, very good. Our errors are resolved and we have one last thing to do, which is check the error states in the component.
[11:51 - 12:05] So heading back into our browser, we have two error states to test in this component. If the initial call to load in the checkout fails, and if the call to remove an item from the list of products in the checkout fails.
[12:06 - 12:15] So as we did in the previous lesson, let's open up Chrome DevTools. It's going to be something like option command J and head over to the network tab.
[12:16 - 12:26] DevTools is telling me that I forgot to restart my server, which is absolutely correct after I installed prop types. So go ahead and start your server back up in your IDE.
[12:27 - 12:41] And now that our server is running again, we are going to look for our checkout call, which actually loads the checkout. And just like we did before, we're going to block it.
[12:42 - 12:50] Go ahead and block that sucker and let's refresh the page. So here's a tip to block network requests.
[12:51 - 13:06] The easiest way to block our second API call is to block the GetAllCheckOut items API call first. The second call, RemoveProductFromCheckOut, takes a product ID in order to remove a specific item from the list of products.
[13:07 - 13:18] But when it fails, it won't actually remove that item from the list. Blocking this GetAllCheckOut items call will make the RemoveProductFromCheckOut call fail when you try to remove a particular item.
[13:19 - 13:32] And then you've got a record of the exact checkout URL with the item ID included, so you can block it exactly. From here, we'll be able to then disable each error individually and test that they work as expected.
[13:33 - 13:54] And we'll need to actually add a couple of items to our checkout to be able to do this as well. So unblock your network request, add a couple of items, re-block it, and head on over to our checkout page.
[13:55 - 14:06] Okay, so now we have both of these things that are being blocked, and we can properly block the item as well. So now we have checkout and checkout plus the product ID.
[14:07 - 14:17] So the first error to check is the failure to load items on the checkout. So uncheck the second blocked network call, this one that we just added, and refresh the checkout page.
[14:18 - 14:25] Great, this looks good, something went wrong. If the items can't be fetched to display in the checkout, this is what we would expect.
[14:26 - 14:38] Now, let's test the second error only when a checkout item fails to be removed from the card. Change what's being blocked, and hmm.
[14:39 - 14:51] So the previous error saying something went wrong, betching the products shows up again, that's not quite right. This is not a great user experience because it's not telling a user what the true problem is.
[14:52 - 15:08] I think that if a checkout item fails to be removed, the list of items should just remain unchanged, but still visible in the card. Especially since we also show an error message in the top right to let the user know that something went wrong, that should be indication enough if this call to remove items fails.
[15:09 - 15:26] So to fix this, let's turn back to our IDE and our code, and where we set our variable to true in the remove item from checkout function, right here, let's change this code. I think we're just going to completely remove it, actually.
[15:27 - 15:35] We don't want this error to be there because, like I said, we're already handling it. So let's go back and look at the browser now.
[15:36 - 15:49] So if we refresh the page, we are still blocking removing a product from the checkout, and when we attempt to do so, the products stay, but the toast here happens. Something went wrong removing product from checkout.
[15:50 - 15:52] Good. That's, I think, much better.
[15:53 - 16:00] So I think that this component refactor is finished. In our next lesson, we'll deal with updating our product form components to use hooks.
[16:01 - 16:03] Forms are always a challenge and react, so this should be interesting.