Skip to content

Latest commit

 

History

History
1200 lines (888 loc) · 27.5 KB

File metadata and controls

1200 lines (888 loc) · 27.5 KB

TechStore React - Advanced useState Features

Complete Step-by-Step Classroom Script (Part 2)


INTRODUCTION (5 minutes)

Script:

"Welcome back everyone! I hope everyone can hear me clearly. Put a yes in the chat if I'm audible.

[Wait for response]

Great! So in our last session, we built the core TechStore functionality - Add to Cart, Wishlist, Search, Filter, and Sort. All using useState.

But you know what? I looked at our project and found some BUGS! Yes, bugs. Real bugs that we need to fix.

Also, I want to add some ADVANCED features today:

  1. Cart Sidebar - Click cart icon, a beautiful sidebar slides in showing all items with +/- buttons
  2. Dark/Light Mode Toggle - Users can switch between dark and light theme
  3. GitHub Deployment - Push our project to GitHub like real developers

But first, let's fix the bugs. Because in the real world, before adding new features, we always fix bugs first!

Let's start!"


PART 1: BUG FIXING SESSION (15 minutes)

Script:

"Open your browser and test the current app. I want you to find bugs. Try everything - search, filter, wishlist, cart.

[Give them 2 minutes]

Did you find any issues? Let me show you what I found:


Bug #1: Brand Filter Not Working!

Try this - select 'Samsung' from the brand dropdown. What happens?

Nothing! All products still show. The dropdown changes but products don't filter.

Let's look at the code. Open App.jsx and find the filter logic:

// CURRENT CODE - BUGGY!
let filteredProducts = products.filter((product) => {
  const matchesSearch = product.brand
    .toLowerCase()
    .includes(searchTerm.toLowerCase());

  return matchesSearch;  // ❌ Where is selectedBrand check?!
});

See the problem? We have selectedBrand state, we have the dropdown, but we NEVER USE IT in the filter!

This is a VERY common bug - you create state, you create UI, but you forget to connect them.

Fix:

// FIXED CODE
let filteredProducts = products.filter((product) => {
  const searchLower = searchTerm.toLowerCase();
  const matchesSearch = 
    product.name.toLowerCase().includes(searchLower) ||
    product.brand.toLowerCase().includes(searchLower);

  // ✅ Now we check the brand!
  const matchesBrand = selectedBrand === 'All' || product.brand === selectedBrand;

  return matchesSearch && matchesBrand;  // ✅ Both conditions!
});

What changed?

  1. matchesSearch now checks BOTH product name AND brand - so searching 'iPhone' works!
  2. Added matchesBrand condition
  3. Return matchesSearch && matchesBrand - both must be true

Update your code and test. Now Samsung filter should work!

[Wait for everyone]


Bug #2: Search Only Works for Brand Names

Try searching 'MacBook'. What happens?

If you had the old code, nothing shows! Why? Because we were only searching in brand name, not product name.

We already fixed this in the code above:

const matchesSearch = 
  product.name.toLowerCase().includes(searchLower) ||  // ✅ Search in name
  product.brand.toLowerCase().includes(searchLower);   // ✅ AND in brand

The || (OR) operator means: match if name contains search term OR brand contains search term.

Now 'MacBook' finds MacBook products, 'iPhone' finds iPhones, 'Apple' finds all Apple products.


Bug #3: Currency Symbol Mismatch

Look at the ProductCard prices. Current price shows ₹ but original price shows $. That's inconsistent!

Open ProductCard.jsx and find:

// BUGGY
<span className='price'>{price}</span>
<span className='original-price'>${originalPrice}</span>  //  Dollar sign!

Fix:

// FIXED
<span className='price'>{price.toLocaleString('en-IN')}</span>
<span className='original-price'>{originalPrice.toLocaleString('en-IN')}</span>

Both now use ₹ and toLocaleString('en-IN') for proper Indian number formatting (1,00,000 instead of 100,000).

[Wait for everyone]


Bug #4: Wishlist Button Not Visible!

Click on the heart icon on any product card. Wait... where IS the heart icon?

Open your browser DevTools (Right click → Inspect). You'll see the button is there but invisible!

Why? Because we never added CSS for the wishlist button!

Open ProductCard.css and add this at the end:

/* ========== WISHLIST BUTTON ========== */
.wishlisted {
  position: absolute;
  top: 20px;
  right: 20px;
  background: rgba(255, 255, 255, 0.1);
  backdrop-filter: blur(10px);
  border: 1px solid rgba(255, 255, 255, 0.15);
  width: 40px;
  height: 40px;
  border-radius: 50%;
  font-size: 1.1rem;
  cursor: pointer;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: all 0.25s ease;
}

.wishlisted:hover {
  background: rgba(255, 255, 255, 0.2);
  transform: scale(1.1);
}

.wishlisted.active {
  background: rgba(239, 68, 68, 0.2);
  border-color: rgba(239, 68, 68, 0.4);
}

Let me explain the key properties:

  • position: absolute - Positions relative to the card (which has position: relative)
  • top: 20px; right: 20px - Places it in top-right corner
  • backdrop-filter: blur(10px) - Creates that frosted glass effect
  • border-radius: 50% - Makes it circular
  • .wishlisted.active - Red tint when product is wishlisted

Save and refresh. Now you should see the heart button!

[Wait for everyone to test]

All bugs fixed! Now let's add new features."


PART 2: CART SIDEBAR FEATURE (25 minutes)

Script:

"Right now when we add items to cart, we see a count and a summary bar at the bottom. But what if user wants to see what's IN the cart? Remove something? Change quantity?

We need a Cart Sidebar - like what you see on Amazon when you click the cart icon. A panel slides in from the right showing all cart items.

Let's build this step by step.


Step 1: Add New State Variables

Open App.jsx. Find where we have all our useState declarations.

After the existing state variables, add this new one:

// Existing states...
const [cartItems, setCartItems] = useState([]);
const [wishlist, setWishlist] = useState([]);
const [searchTerm, setSearchTerm] = useState('');
const [selectedBrand, setSelectedBrand] = useState('All');
const [sortBy, setSortBy] = useState('default');

// NEW: Add this for cart sidebar
const [isCartOpen, setIsCartOpen] = useState(false);

What is this?

  • isCartOpen - A BOOLEAN that tracks if cart sidebar is visible
  • Initially false - cart is closed
  • When user clicks cart icon, we set it to true - cart opens

[Wait for everyone]


Step 2: Add Remove and Update Quantity Functions

Currently we can only ADD to cart. But in the sidebar, users need to:

  1. Remove items completely
  2. Increase/decrease quantity

Add these new functions after your addToCart function:

// NEW: Remove item from cart
function removeFromCart(productId) {
  setCartItems(cartItems.filter((item) => item.id !== productId));
}

This is simple - use filter to keep all items EXCEPT the one with matching ID.

// NEW: Update quantity in cart
function updateQuantity(productId, newQuantity) {
  if (newQuantity < 1) {
    removeFromCart(productId);  // If quantity becomes 0, remove item
    return;
  }
  setCartItems(
    cartItems.map((item) =>
      item.id === productId 
        ? { ...item, quantity: newQuantity }  // Update this item
        : item  // Keep others unchanged
    )
  );
}

This is more complex. Let me explain:

  1. First we check if newQuantity < 1. If someone clicks minus and quantity would become 0, we remove the item instead.
  2. Otherwise, we use map to go through all cart items.
  3. For the matching item, we use spread operator { ...item, quantity: newQuantity } to create a new object with updated quantity.
  4. Other items stay unchanged.

The spread operator is KEY here - we copy all properties of the item and override just the quantity.

[Wait for everyone]


Step 3: Update Cart Icon to Open Sidebar

Find the cart button in the navbar:

<button className='nav-btn icon-btn'>
  🛒
  {cartCount > 0 && <span className='badge'>{cartCount}</span>}
</button>

Add the onClick handler:

<button 
  className='nav-btn icon-btn'
  onClick={() => setIsCartOpen(true)}  // ✅ Opens sidebar!
>
  🛒
  {cartCount > 0 && <span className='badge'>{cartCount}</span>}
</button>

Now clicking the cart icon will set isCartOpen to true.

[Wait for everyone]


Step 4: Create the Cart Sidebar JSX

This is the main part. After the navbar closing tag </nav>, add the cart sidebar:

{/* Cart Sidebar */}
{isCartOpen && (
  <div className='cart-overlay' onClick={() => setIsCartOpen(false)}>
    <div className='cart-sidebar' onClick={(e) => e.stopPropagation()}>
      
      {/* Header */}
      <div className='cart-header'>
        <h2>Your Cart ({cartCount})</h2>
        <button className='cart-close' onClick={() => setIsCartOpen(false)}></button>
      </div>

      {/* Cart Items */}
      <div className='cart-items'>
        {cartItems.length === 0 ? (
          <div className='cart-empty'>
            <span className='cart-empty-icon'>🛒</span>
            <p>Your cart is empty</p>
            <button className='btn-primary' onClick={() => setIsCartOpen(false)}>
              Continue Shopping
            </button>
          </div>
        ) : (
          cartItems.map((item) => (
            <div key={item.id} className='cart-item'>
              <img src={item.image} alt={item.name} className='cart-item-image' />
              <div className='cart-item-details'>
                <h4>{item.name}</h4>
                <p className='cart-item-price'>{item.price.toLocaleString('en-IN')}
                </p>
                <div className='quantity-controls'>
                  <button onClick={() => updateQuantity(item.id, item.quantity - 1)}></button>
                  <span>{item.quantity}</span>
                  <button onClick={() => updateQuantity(item.id, item.quantity + 1)}>
                    +
                  </button>
                </div>
              </div>
              <button className='cart-item-remove' onClick={() => removeFromCart(item.id)}>
                🗑️
              </button>
            </div>
          ))
        )}
      </div>

      {/* Footer */}
      {cartItems.length > 0 && (
        <div className='cart-footer'>
          <div className='cart-subtotal'>
            <span>Subtotal:</span>
            <span className='cart-subtotal-price'>{cartTotal.toLocaleString('en-IN')}
            </span>
          </div>
          <button className='btn-checkout-full'>
            Proceed to Checkout
          </button>
        </div>
      )}

    </div>
  </div>
)}

This is a lot of code! Let me explain piece by piece:

Line 1: {isCartOpen && (...)} - Only render sidebar when isCartOpen is true. This is conditional rendering!

Line 2: <div className='cart-overlay' onClick={() => setIsCartOpen(false)}> - The dark overlay behind the sidebar. Clicking it closes the sidebar.

Line 3: <div className='cart-sidebar' onClick={(e) => e.stopPropagation()}> - The actual sidebar.

What is e.stopPropagation()? ⭐ IMPORTANT!

When you click INSIDE the sidebar, the click event would normally 'bubble up' to the overlay and trigger its onClick (which closes the sidebar). We don't want that! stopPropagation() stops the event from bubbling up.

Cart Items Section:

{cartItems.length === 0 ? (
  // Empty cart message
) : (
  // Map through and show items
)}

This is a ternary operator for conditional rendering:

  • If cart is empty → show empty message
  • If cart has items → map through and display them

Quantity Controls:

<button onClick={() => updateQuantity(item.id, item.quantity - 1)}></button>
<span>{item.quantity}</span>
<button onClick={() => updateQuantity(item.id, item.quantity + 1)}>+</button>

Minus button passes quantity - 1, plus button passes quantity + 1. Our function handles the rest!

[Wait for everyone to type this. Help those who are stuck.]


Step 5: Update Cart Summary Bar

Find the cart summary bar (the blue bar at bottom) and update the button:

{cartItems.length > 0 && (
  <div className='cart-summary'>
    <div className='cart-summary-content'>
      <span>🛒 {cartCount} items in cart</span>
      <span className='cart-total'>
        Total: ₹{cartTotal.toLocaleString('en-IN')}
      </span>
      <button className='btn-checkout' onClick={() => setIsCartOpen(true)}>
        View Cart →
      </button>
    </div>
  </div>
)}

Changed the button text to 'View Cart' and added onClick to open sidebar.


Step 6: Add Cart Sidebar CSS

Open App.css and add this at the end:

/* ========== CART SIDEBAR ========== */
.cart-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(4px);
  z-index: 2000;
  animation: fadeIn 0.2s ease;
}

@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

.cart-sidebar {
  position: fixed;
  top: 0;
  right: 0;
  width: 420px;
  max-width: 90vw;
  height: 100vh;
  background: var(--bg-card);
  border-left: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  animation: slideIn 0.3s ease;
  z-index: 2001;
}

@keyframes slideIn {
  from { transform: translateX(100%); }
  to { transform: translateX(0); }
}

.cart-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 24px;
  border-bottom: 1px solid var(--border);
}

.cart-header h2 {
  font-size: 1.3rem;
  font-weight: 700;
}

.cart-close {
  background: rgba(255, 255, 255, 0.1);
  border: none;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  font-size: 1rem;
  cursor: pointer;
  color: var(--text-secondary);
  transition: all 0.2s ease;
}

.cart-close:hover {
  background: rgba(255, 255, 255, 0.2);
  color: var(--text-primary);
}

.cart-items {
  flex: 1;
  overflow-y: auto;
  padding: 16px;
}

.cart-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  gap: 16px;
  color: var(--text-secondary);
}

.cart-empty-icon {
  font-size: 4rem;
  opacity: 0.3;
}

.cart-item {
  display: flex;
  gap: 16px;
  padding: 16px;
  background: var(--bg-elevated);
  border-radius: 12px;
  margin-bottom: 12px;
  position: relative;
}

.cart-item-image {
  width: 80px;
  height: 80px;
  object-fit: contain;
  background: var(--bg-secondary);
  border-radius: 8px;
  padding: 8px;
}

.cart-item-details {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.cart-item-details h4 {
  font-size: 0.9rem;
  font-weight: 600;
  line-height: 1.3;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.cart-item-price {
  color: var(--accent);
  font-weight: 700;
  font-size: 1rem;
}

.quantity-controls {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: auto;
}

.quantity-controls button {
  width: 28px;
  height: 28px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg-secondary);
  color: var(--text-primary);
  font-size: 1rem;
  cursor: pointer;
  transition: all 0.2s ease;
  display: flex;
  align-items: center;
  justify-content: center;
}

.quantity-controls button:hover {
  background: var(--accent);
  border-color: var(--accent);
}

.quantity-controls span {
  font-weight: 600;
  min-width: 24px;
  text-align: center;
}

.cart-item-remove {
  position: absolute;
  top: 12px;
  right: 12px;
  background: transparent;
  border: none;
  font-size: 1rem;
  cursor: pointer;
  opacity: 0.5;
  transition: opacity 0.2s ease;
}

.cart-item-remove:hover {
  opacity: 1;
}

.cart-footer {
  padding: 24px;
  border-top: 1px solid var(--border);
  background: var(--bg-elevated);
}

.cart-subtotal {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 16px;
  font-size: 1.1rem;
}

.cart-subtotal-price {
  font-weight: 700;
  font-size: 1.4rem;
  color: var(--text-primary);
}

.btn-checkout-full {
  width: 100%;
  padding: 16px;
  background: var(--accent);
  color: white;
  border: none;
  border-radius: 12px;
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s ease;
}

.btn-checkout-full:hover {
  background: var(--accent-hover);
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(0, 113, 227, 0.3);
}

Save all files and test!

Testing:

  1. Add some products to cart
  2. Click the cart icon in navbar
  3. Sidebar should slide in from the right!
  4. Click + to increase quantity
  5. Click - to decrease (goes to 0 = removes item)
  6. Click 🗑️ to remove directly
  7. Click outside sidebar or ✕ to close

Everything working? Excellent! 🎉

[Wait for everyone to test]"


PART 3: DARK/LIGHT MODE TOGGLE (20 minutes)

Script:

"Now let's add something cool - a theme toggle! Users can switch between dark mode (current) and light mode.

This is a CLASSIC use of useState with boolean. Watch carefully.


Step 1: Add Dark Mode State

Add this new state variable in App.jsx:

// NEW: Dark Mode Toggle
const [isDarkMode, setIsDarkMode] = useState(true);  // true = dark mode (default)

Why true as initial value? Because our app is currently styled for dark mode. That's the default.


Step 2: Apply Dynamic Class to App Container

Find your app container div:

return (
  <div className='app'>

Change it to:

return (
  <div className={`app ${isDarkMode ? 'dark' : 'light'}`}>

What is this?

  • Template literal with conditional
  • If isDarkMode is true → className becomes 'app dark'
  • If isDarkMode is false → className becomes 'app light'

Now we can write separate CSS for .app.dark and .app.light!


Step 3: Add Theme Toggle Button

In the navbar, find the nav-actions div and add the toggle button FIRST (before wishlist button):

<div className='nav-actions'>
  {/* NEW: Dark Mode Toggle */}
  <button
    className='nav-btn icon-btn theme-toggle'
    onClick={() => setIsDarkMode(!isDarkMode)}
    title={isDarkMode ? 'Switch to Light Mode' : 'Switch to Dark Mode'}
  >
    {isDarkMode ? '☀️' : '🌙'}
  </button>

  {/* Wishlist Button... */}
  {/* Cart Button... */}
  {/* etc... */}
</div>

Let me explain:

onClick={() => setIsDarkMode(!isDarkMode)}

The ! operator flips the boolean:

  • If isDarkMode is true, !isDarkMode is false
  • If isDarkMode is false, !isDarkMode is true

So clicking the button toggles between true and false!

{isDarkMode ? '☀️' : '🌙'}

Conditional rendering for the icon:

  • In dark mode → show ☀️ (suggesting 'click for light mode')
  • In light mode → show 🌙 (suggesting 'click for dark mode')

This is like how your phone shows a sun when in dark mode to suggest switching to light!

[Wait for everyone]


Step 4: Add Light Mode CSS

Open App.css and add these styles at the end:

/* Theme Toggle Button */
.theme-toggle {
  font-size: 1.1rem;
}

/* ========== LIGHT MODE ========== */
.app.light {
  --bg-primary: #f5f5f7;
  --bg-secondary: #ffffff;
  --bg-card: #ffffff;
  --bg-elevated: #f0f0f2;

  --text-primary: #1d1d1f;
  --text-secondary: rgba(0, 0, 0, 0.6);
  --text-muted: rgba(0, 0, 0, 0.4);

  --border: rgba(0, 0, 0, 0.1);
  --border-hover: rgba(0, 0, 0, 0.2);

  --shadow-sm: 0 4px 12px rgba(0, 0, 0, 0.08);
  --shadow-md: 0 8px 24px rgba(0, 0, 0, 0.12);
  --shadow-lg: 0 16px 48px rgba(0, 0, 0, 0.15);
}

.app.light .navbar {
  background: rgba(255, 255, 255, 0.85);
}

.app.light .hero {
  background: var(--bg-primary);
}

.app.light .hero-title {
  color: var(--text-primary);
}

.app.light .product-card {
  background: #ffffff;
  box-shadow: var(--shadow-sm);
}

.app.light .product-card:hover {
  box-shadow: var(--shadow-lg);
}

.app.light .image-container {
  background: linear-gradient(180deg, #f5f5f7 0%, #ffffff 100%);
}

.app.light .section-title {
  color: var(--text-primary);
}

.app.light .section-subtitle {
  color: var(--text-secondary);
}

.app.light .nav-btn {
  color: var(--text-secondary);
}

.app.light .nav-btn:hover {
  color: var(--text-primary);
  background: rgba(0, 0, 0, 0.05);
}

.app.light .search-input,
.app.light .filter-select {
  background: var(--bg-elevated);
  color: var(--text-primary);
}

.app.light .btn-secondary {
  color: var(--text-primary);
  border-color: var(--border);
}

.app.light .cart-close {
  background: rgba(0, 0, 0, 0.05);
  color: var(--text-secondary);
}

.app.light .quantity-controls button {
  background: var(--bg-elevated);
  color: var(--text-primary);
}

.app.light .footer {
  background: var(--bg-secondary);
  color: var(--text-muted);
}

What's happening here?

The magic is in CSS Variables!

In dark mode (default), our :root has:

--bg-primary: #000000;
--text-primary: #ffffff;

In .app.light, we OVERRIDE these variables:

--bg-primary: #f5f5f7;
--text-primary: #1d1d1f;

Now EVERYTHING that uses var(--bg-primary) automatically changes when we toggle modes!

This is why CSS variables are so powerful for theming!

[Wait for everyone]


Step 5: Fix ProductCard Text for Light Mode

Open ProductCard.css and update the text colors to use CSS variables:

Find and replace these:

/* Change FROM hardcoded colors TO variables */

.product-name {
  color: var(--text-primary);  /* Was: rgba(255, 255, 255, 0.95) */
}

.rating-value {
  color: var(--text-secondary);  /* Was: rgba(255, 255, 255, 0.6) */
}

.price {
  color: var(--text-primary);  /* Was: white */
}

.original-price {
  color: var(--text-muted);  /* Was: rgba(255, 255, 255, 0.4) */
}

Now product card text will adapt to both themes!


Testing Dark/Light Mode

Save all files and test:

  1. Click the ☀️ button in navbar
  2. Entire app should switch to light theme!
  3. Click 🌙 to go back to dark mode
  4. Open cart sidebar in both modes - should look good in both

Everything working? Beautiful! 🎉

This is EXACTLY how apps like Twitter, Instagram, WhatsApp handle dark mode!"


PART 4: DEPLOYING TO GITHUB (15 minutes)

Script:

"Now let's be REAL developers and push our code to GitHub! This is how teams collaborate in real life.


Step 1: Initialize Git Repository

Open your terminal (Terminal in VS Code: View → Terminal or Ctrl+`)

Navigate to your project folder and run:

git init

This creates a .git folder and initializes Git in your project.


Step 2: Stage All Files

git add .

The . means 'all files'. This stages (prepares) all your files for commit.


Step 3: Make Your First Commit

git commit -m 'Initial commit: TechStore React App with cart, wishlist, dark mode'

The -m flag lets you write a commit message. Always write descriptive messages!


Step 4: Create GitHub Repository

  1. Go to github.com and sign in
  2. Click the + icon → New repository
  3. Repository name: techstore-react
  4. Keep it Public
  5. DON'T check 'Add README' (we already have code)
  6. Click Create repository

GitHub will show you commands. Copy the ones that look like:

git remote add origin https://github.com/YOUR_USERNAME/techstore-react.git
git branch -M main
git push -u origin main

Step 5: Push to GitHub

Run those commands in your terminal.

You might be asked for your GitHub username and password. For password, use a Personal Access Token:

  1. Go to GitHub → Settings → Developer Settings → Personal Access Tokens → Tokens (classic)
  2. Generate new token with 'repo' scope
  3. Copy and use that as your password

After successful push, refresh your GitHub page - your code is online!


Making Future Changes

Whenever you make changes and want to push:

git add .
git commit -m 'Description of what you changed'
git push

That's it! Three commands every time.

Example:

git add .
git commit -m 'Added 5 new products'
git push

Your changes are now on GitHub!"


SUMMARY & CONCLUSION (10 minutes)

Script:

"Let me summarize EVERYTHING we learned today:

Part 1: Bug Fixes

Bug Issue Solution
Brand Filter State existed but never used Added matchesBrand condition
Search Only searched brand name Added `
Currency Mixed $ and ₹ Consistent ₹ with toLocaleString
Wishlist Button No CSS Added glassmorphism styles

Part 2: Cart Sidebar

New State:

const [isCartOpen, setIsCartOpen] = useState(false);

New Functions:

function removeFromCart(productId) {...}
function updateQuantity(productId, newQuantity) {...}

Key Concepts:

  • e.stopPropagation() - Prevents event bubbling
  • Slide-in animation with CSS keyframes
  • Conditional rendering for empty vs filled cart

Part 3: Dark/Light Mode

New State:

const [isDarkMode, setIsDarkMode] = useState(true);

Key Technique:

<div className={`app ${isDarkMode ? 'dark' : 'light'}`}>

CSS Variables make theming easy - just override variables for different themes!

Part 4: Git Commands

git init              # Initialize repository
git add .             # Stage all files
git commit -m 'msg'   # Commit with message
git push              # Push to GitHub

All useState Variables in Our App

Variable Type Purpose
cartItems Array Products in cart
wishlist Array IDs of wishlisted products
searchTerm String Search input value
selectedBrand String Brand filter selection
sortBy String Sort preference
isDarkMode Boolean Theme toggle
isCartOpen Boolean Cart sidebar visibility

7 useState variables controlling the ENTIRE app experience!

This is the power of React State. With just these 7 variables, we have:

  • 🛒 Full cart functionality
  • ❤️ Wishlist system
  • 🔍 Real-time search
  • 🏷️ Brand filtering
  • ↕️ Price/rating sorting
  • 🌙 Dark/Light theme
  • 📱 Slide-in sidebar

And this is just useState! Wait until we learn useEffect... 😉

Great work everyone! 🎉

Any questions?"


COMPLETE CODE FILES FOR REFERENCE

App.jsx Key Changes Summary

// NEW State Variables
const [isDarkMode, setIsDarkMode] = useState(true);
const [isCartOpen, setIsCartOpen] = useState(false);

// NEW Functions
function removeFromCart(productId) {
  setCartItems(cartItems.filter((item) => item.id !== productId));
}

function updateQuantity(productId, newQuantity) {
  if (newQuantity < 1) {
    removeFromCart(productId);
    return;
  }
  setCartItems(
    cartItems.map((item) =>
      item.id === productId 
        ? { ...item, quantity: newQuantity } 
        : item
    )
  );
}

// FIXED Filter Logic
let filteredProducts = products.filter((product) => {
  const searchLower = searchTerm.toLowerCase();
  const matchesSearch = 
    product.name.toLowerCase().includes(searchLower) ||
    product.brand.toLowerCase().includes(searchLower);
  const matchesBrand = selectedBrand === 'All' || product.brand === selectedBrand;
  return matchesSearch && matchesBrand;
});

// Dynamic App Class for Theme
<div className={`app ${isDarkMode ? 'dark' : 'light'}`}>

// Theme Toggle Button
<button onClick={() => setIsDarkMode(!isDarkMode)}>
  {isDarkMode ? '☀️' : '🌙'}
</button>

HOMEWORK / PRACTICE EXERCISES

  1. Add 'Clear Cart' button - One click removes ALL items from cart
  2. Persist theme preference - When user refreshes, theme should stay (Hint: localStorage - we'll learn this with useEffect)
  3. Add product comparison - Users can select up to 3 products to compare side by side
  4. Add toast notifications - Show 'Added to cart!' message when user adds item

Created for React DAY 11 - Part 2 Advanced useState Patterns & GitHub Deployment