Creating a Cart Context and Provider
The first step is to implement a Cart Context, which will be in a file CartContext.tsx in the src/app/components/ directory.
Implementing a Cart Context
The CartContext will be a Client Component so we'll start the file with "use client"; at the top. Remember, React Server Components don't support context.
We'll import React, createContext, and useState, as well as importing the Cart type:
To make things easier, we'll create a custom hook called useCartState that will invoke useState:
Then we'll create a CartContext in order to provide it. The return type will be the useCartState custom hook:
Now we'll create the useCart hook that will be used to access the cart. It will use the useContext hook to get the context from the CartContext. Its output will be an array with the cart and its setter, or it will throw an error.
Finally, we'll define the CartProvider. This will be a client component that will wrap any downstream children that it's going to provide them the cart:
What's cool about client components in the App Router is that they can transclude (or "project") children that are either client components or RSCs.
Updating the Layout Component
Now that we have our CartProvider, we'll need to update the Layout to use it.
Inside of Layout.tsx, import the CartProvider then wrap the Header and main content with it.
Note that we can remove the cart prop from the Header since it will be provided by the context!
Updating the Header Component
Inside of Header.tsx, we can import the useCart hook from CartContext:
Then we'll get rid of the cart prop and get it from the useCart hook instead:
We'll also remove the cart prop from the CartPopup component:
Updating the Cart Popup Component
The same kind of thing needs to be done with our CartPopup component at CartPopup.tsx.
In this case, we both need to bring in the cart because we want to display it, but we also want to set the cart based on the output of the clearCartAction event handler. So, we'll bring in setCart as well. And then down here, we will set the cart based on the output of the clearCart action.
Update the Add To Cart Component
We also need to bring in useCart to the Add to Cart component at AddToCart.tsx.
This time we'll bring in just setCart from the hook, since the cart will be set to the output of the addToCartAction:
Checking Our Work
Checking the Donuts & Dragoons app, we can see a 0 in the header because we haven't added anything to the cart. However, when we navigate into a product and add it to the cart, the count jumps to 3!
Let's jump back to our code to determine why.
Inside of src/api/cart.ts we can see that the starting state of the cart already includes two items:
This means that when we performed the addToCart action, it added the Elf t-shirt to our in-memory cart, so the total count became three.
Browsing around the site, the cart count remains consistent because the cart context is shared between every single route in the application, including the homepage and any product detail page.
However, when we refresh the page, the cart count goes back to zero.
In the next exercise, we'll work on seeding the cart context with the initial data from the server in order to maintain the correct cart count.