Often when we have too much business logic in one file, it tends to become hard to read and manage. On top of that, there might be some parts of that logic that we want to abstract so we can use it in other parts of our application.
We can abstract our shopping cart state by creating a custom React hook that we can use in our store to more easily maintain our app. You'll see how much simpler our Home page looks after moving the cart logic out!
Instructor: [0:00] We're going to start off with our Space Jelly Shop Next.js App. We have three items that we're trying to sell. Anytime we add one of those items to our shopping cart, our shopping cart gets updated so that we can see how many items and the total cost.
[0:11] With those cart totals, we can even hit the Checkout button, which redirects to Stripe so that we can purchase that item. Inside our code, we're using the useState hook in order to maintain that cart state. With that cart state, we're creating an array of our cart items, which we're using to calculate the subtotal and the total number of items.
[0:27] We also have an Add To Cart function which allows us to add items to our cart and finally a Checkout function which allows us to initiate the checkout sequence for Stripe. Now, having the code inside of our home page is working OK. We're able to add items to our cart, and we're able to checkout with those items.
[0:41] As our cart grows, it becomes a little bit more complex, and it's not probably the right place to maintain that logic inside of our home page. Additionally, if we ever wanted to use this solution on another page, we wouldn't be able to do that without copying and pasting the entire code. Instead, using React hooks, we can build our own hook, where we can abstract that logic and use it anywhere in our app.
[1:00] To get started building our own hook, the first thing I'm going to do is create a new folder, simply called hooks. Inside that folder, I'm going to create a new file, and I'm going to call to use-cart.js. The core of any React hook is a function. The only thing that makes it special is we prefix it with use, so it has access to React internals.
[1:18] We're going to export a default function, and we're going to call it useCart. Just to test this out, let's return our cart. Back inside of our home page, we can import our useCart function from one level-up hooks use-cart.js. At the top of our home page component, we can add constant cart test = useCart.
[1:41] Like anything else, we can add a console.log statement, where we can see what the value is inside. When we open back up our app, we can look inside of our developer tools, and we can see that it's logging out our cart. Back on our home page, what's happening is we're running this function called useCart, and it's returning that value.
[1:57] As I mentioned before, the thing that makes a hook special is we prefix it with this word use. What this does is provide us additional access to React internals, so we can use the useState hook. Let's import our useState hook from 'react'. Now that we have access to useState, we can put our logic in here to maintain our entire cart state.
[2:16] What we're going to do is we're going to first copy over our default cart as well as our useState instance of our cart. I'm going to cut out this default cart and paste it right at the top of our useCart file. I'm also going to grab our cart state and paste that in as well.
[2:29] Instead of returning our cart state, we're going to return an object. Inside that object, we're going to return our cart in our updateCart function. Instead of our cart test constant, we're going to change that to a destructured object, where we're going to grab the cart and our updateCart function, meaning we can also get rid of this console log statement now.
[2:47] At this point, we're no longer using our useState hook directly inside of our home page. Now if we open back up our shop on our development server, we can scroll down. We can try adding some products to the cart. We can still that it still works.
[2:59] When we're returning these values from our cart, we're giving our home page the exact same access to that useState functionality that we had before. Now, we can copy all of this cart logic directly into that useCart hook.
[3:10] I'm going to take the cartItems all the way down to the checkOut function. I'm going to cut those values outside of my home page, and I'm going to paste them right inside of my hook. Back inside of the home page, we still need some of these values, particularly subtotal, totalItems, addToCart, and checkOut.
[3:26] In our return statement, we're going to add subtotal and our totalItems, as well as addToCart and our checkOut function. Back inside of our home page, we're no longer using cart or updateCart. We can replace that with subtotal, totalItems, addToCart, and checkOut.
[3:45] Finally, we're also using the products array to look up our product information, particularly the price per unit. We're going to copy our reference to that products.json file and we're also going to include it inside of useCart. Back in our Space Jelly shop, we can see that we still have our cart.
[3:59] If we scroll down, we can add some items to that cart. We can see that it still gets updated. Now if we hit the Checkout button, we can see that we get an error. What happened? Inside of our cart, we can see that we're using the initiateCheckOut function, but we didn't make that available to our cart specifically. We only have that available inside of our home page.
[4:16] Back in our home page, since we're no longer using that function in that file, we can remove it and import it at the top of our useCart hook. Next time we try to checkOut and hit the button, we get redirected to Stripe with our shopping cart.
[4:28] In review, we had a lot of logic inside of our home page that pertained to our cart state. The more that grew, the more complex it got. It's probably not the right place to maintain that inside of our home page.
[4:38] We created a new React hook, which is a function with a special prefix, where we were then able to use the useState hook inside of that function, copy over all of our logic, including our cartItems, subtotal, totalItems, and addToCart function, allowing us to import and use them in our home page, allowing us to still add items to our cart and check out with Stripe.
Why are you importing: import { useState } from 'react' to the Index.js file?
import { useState } from 'react'
This is so that the instructor can use this React Hook to set state for his cart.
Error As:
Unhandled Runtime Error ReferenceError: cartItems is not defined