Week 7 Last week, our page asked a server for a list and showed it. This week we're building the thing every store has: a cart. And we're building it together, one piece at a time. You'll start with a page that just shows a shirt. By the end of the meeting you'll be able to pick a size, add it to a cart, add it again and watch the quantity go up, and remove it. Every one of those is a few lines you typed yourself. The products and prices are made up, and the cart only lives in React state (so refreshing the page empties it). Adding something doesn't reserve a shirt, place an order, or charge anyone. We'll get to the real store cart later. Getting started Save and commit your Week 6 work. Open a terminal at the top of practice-page and make a new branch from it: git status git switch -c cart-practice If Git says that branch already exists, use git switch cart-practice instead. Now we need a starting point. In react-page/src, replace App.jsx and App.css with the starter versions below, and make a new file called products.js. Keep main.jsx and styles.css the way they are. (Your Week 6 work is still saved in Git, so nothing is lost.) The same three files are also in week-07-starter.zip if you'd rather download than copy. src/products.js // Made-up practice products. Prices are in cents, so 2400 means $24.00. export const products = [ { id: "demo-shirt", name: "Practice T-shirt", variants: [ { id: "demo-shirt-small", label: "Small", priceMinor: 2400, available: true }, { id: "demo-shirt-medium", label: "Medium", priceMinor: 2400, available: true }, { id: "demo-shirt-large", label: "Large", priceMinor: 2400, available: false }, ], }, ]; src/App.css section + section { margin-top: 24px; } label { display: block; font-weight: 600; margin: 16px 0 8px; } select { display: block; width: 100%; font: inherit; color: inherit; background: white; padding: 12px; border: 1px solid #64748b; border-radius: 8px; margin-bottom: 16px; } select:focus-visible { outline: 3px solid #1d4ed8; outline-offset: 3px; } .remove-button { color: #1d4ed8; background: #eff6ff; border: 1px solid #1d4ed8; } .cart-subtotal { font-size: 1.2rem; font-weight: 700; } button:disabled { background: #475569; cursor: not-allowed; } src/App.jsx (starter) import "./App.css"; const product = products[0]; function formatPrice(priceMinor) { return (priceMinor / 100).toLocaleString("en-US", { style: "currency", currency: "USD", }); } export default function App() { return (

Practice cart

Made-up products. This cart lives in React state, so refreshing the page empties it.

{product.name}

{formatPrice(product.variants[0].priceMinor)}

{/* Step 1: the size dropdown goes here */}

Your cart

Your cart is empty.

); } Start the server from react-page: cd react-page npm run dev Open the address the terminal prints. You should see Practice cart, a Practice T-shirt for $24.00, and "Your cart is empty." That's it for now. There's no dropdown and no button yet, because that's what we're about to write. If npm isn't found, check the Setup page. Leave the dev server running the whole time. Every time you save, the page updates by itself. What a cart has to know A product list and a cart sound like the same thing, but they do pretty different jobs. The product list describes what you can buy. The cart describes what this person picked and how many. Open products.js. There's one product, the Practice T-shirt, and it has three variants. A variant is one specific option of a product that you can actually buy, like the Small T-shirt. All three sizes belong to demo-shirt, but each one has its own variant ID. That's the big idea for today: the cart needs to remember the variant, not just the product. If we only saved demo-shirt, a Small and a Medium would look like the same thing, and someone ordering one of each would get two of whatever we guessed. available is a Boolean ( true or false). Large is false, so it'll show up as sold out. priceMinor is the price in cents, so 2400 means $24.00. We store whole cents because decimals in JavaScript can get weird (try 0.1 + 0.2 in the Console sometime). formatPrice in the starter turns cents into dollars, and it's the only place we divide by 100. These field names are our practice format, not Fourthwall's. The real API has its own fields, and you always go by its docs. Here's what we're building toward. The cart line remembers the variant ID. Adding Small again doesn't make a second line, it just bumps the quantity. Adding Medium does make a second line. Step 1: Pick a size First, the page needs to remember which size is picked. That's state, same as the search box in Week 5. At the top of App.jsx, add the useState import above the products import, so the imports look like this: import { useState } from "react"; import { products } from "./products.js"; Next, inside App, right under the export default function App() { line, add the state and a function to update it: const [selectedVariantId, setSelectedVariantId] = useState(product.variants[0].id); function handleVariantChange(event) { setSelectedVariantId(event.target.value); } selectedVariantId starts as the first variant's ID, so the page starts on Small. Now find these two lines in the product section:

{formatPrice(product.variants[0].priceMinor)}

{/* Step 1: the size dropdown goes here */} Delete both of them and put this in their place:

Selected: {selectedVariantId}

Save. You should see a Size dropdown with Small, Medium, and Large, and Large should be greyed out with "(sold out)". Change it to Medium, and the "Selected:" line should change to demo-shirt-medium. So what did we just write? This is the same controlled input from Week 5, just a dropdown this time. value comes from state, and handleVariantChange puts event.target.value back into state. We .map() over the variants to make one