≑ Menu

πŸ›’ Building a Simple E-Commerce Cart in React

Welcome to the ultimate guide for building a core feature of any modern web application: a fully functional shopping cart!

In the world of e-commerce, the shopping cart is more than just a listβ€”it’s the critical hub where user interaction meets application state. As React developers, understanding how to manage this dynamic state is fundamental.

This tutorial dives into a clean, component-driven approach to building a shopping cart using React Hooks (useState). You’ll learn essential techniques like:

  • State Immortality: Updating arrays correctly in React.

  • Prop Drilling: Passing functions and data down through components.

  • Component Composition: Structuring your application into reusable pieces (ProductCard, ProductList, and Cart).

By the end of this guide, you won’t just have a working cart; you’ll have mastered the state management principles needed to build any complex feature in a modern React application.

Let’s dive in and start coding!

πŸ—οΈ Project Structure and Setup

 

We need four files in total for this improved structure:

src/
β”œβ”€β”€ components/
β”‚   β”œβ”€β”€ Cart.js
β”‚   β”œβ”€β”€ ProductList.js
β”‚   └── ProductCard.js  <-- NEW COMPONENT
β”œβ”€β”€ data.js
└── App.js

1. The Data (data.js)

 

Our list of products remains the same:

// data.js
export const products = [
  { id: 1, name: 'Laptop', price: 1200 },
  { id: 2, name: 'Mouse', price: 25 },
  { id: 3, name: 'Keyboard', price: 75 },
  { id: 4, name: 'Monitor', price: 300 },
];

πŸ“¦ Presentation Components

 

2. Product Card (components/ProductCard.js)

 

This component handles the display of a single product and provides the button to interact with the cart.

// components/ProductCard.js
import React from 'react';

const ProductCard = ({ product, onAddToCart }) => {
  return (
    <div 
      style={{ border: '1px solid #eee', padding: '15px', borderRadius: '5px' }}
    >
      <h3>{product.name}</h3>
      <p>**${product.price.toFixed(2)}**</p>
      {/* The button triggers the onAddToCart function passed down from App.js */}
      <button 
        onClick={() => onAddToCart(product)} 
        style={{ padding: '10px', backgroundColor: 'teal', color: 'white', border: 'none', cursor: 'pointer' }}
      >
        Add to Cart
      </button>
    </div>
  );
};

export default ProductCard;

3. Product List (components/ProductList.js)

 

This component is the container. It receives the list of products and the onAddToCart function as props, and its sole job is to map over the list and render a ProductCard for each item.

// components/ProductList.js
import React from 'react';
import ProductCard from './ProductCard'; // <-- Import the Card

const ProductList = ({ products, onAddToCart }) => {
  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: '20px' }}>
      {products.map(product => (
        <ProductCard 
          key={product.id} 
          product={product} 
          onAddToCart={onAddToCart} 
        />
      ))}
    </div>
  );
};

export default ProductList;

4. Cart Display (components/Cart.js)

This component remains the same, responsible for displaying the items and the total.

// components/Cart.js
import React from 'react';

const Cart = ({ items, total, onRemoveFromCart }) => {
  return (
    <div>
      {/* ... (Cart rendering logic remains the same) ... */}
      {items.length === 0 ? (
        <p>Your cart is empty.</p>
      ) : (
        <>
          <ul style={{ listStyle: 'none', padding: 0 }}>
            {items.map(item => (
              <li 
                key={item.id} 
                style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '10px', borderBottom: '1px dotted #ccc', paddingBottom: '5px' }}
              >
                <span>{item.name} (x{item.quantity})</span>
                <span>
                  **${(item.price * item.quantity).toFixed(2)}**
                  <button 
                    onClick={() => onRemoveFromCart(item.id)}
                    style={{ marginLeft: '10px', backgroundColor: 'red', color: 'white', border: 'none', cursor: 'pointer', padding: '5px 8px' }}
                  >
                    -
                  </button>
                </span>
              </li>
            ))}
          </ul>
          <hr />
          <h3>Total: **${total.toFixed(2)}**</h3>
        </>
      )}
    </div>
  );
};

export default Cart;

βš›οΈ The Main Application Logic (App.js)

 

This file is the central hub for state management and passing down the necessary props (data and functions) to its children.

// App.js
import React, { useState } from 'react';
import { products } from './data';
import ProductList from './components/ProductList'; // <-- Imported
import Cart from './components/Cart';

function App() {
  const [cartItems, setCartItems] = useState([]);

  // Function to add an item to the cart (Logic remains the same)
  const addToCart = (product) => {
    const existingItem = cartItems.find(item => item.id === product.id);

    if (existingItem) {
      setCartItems(
        cartItems.map(item =>
          item.id === product.id
            ? { ...item, quantity: item.quantity + 1 }
            : item
        )
      );
    } else {
      setCartItems([...cartItems, { ...product, quantity: 1 }]);
    }
  };

  // Function to remove an item or decrease quantity (Logic remains the same)
  const removeFromCart = (productId) => {
    setCartItems(
      cartItems.reduce((acc, item) => {
        if (item.id === productId) {
          if (item.quantity > 1) {
            acc.push({ ...item, quantity: item.quantity - 1 });
          }
        } else {
          acc.push(item);
        }
        return acc;
      }, [])
    );
  };

  // Calculate the total cost
  const cartTotal = cartItems.reduce(
    (total, item) => total + item.price * item.quantity,
    0
  );

  return (
    <div className="App" style={{ padding: '20px' }}>
      <h1>βš›οΈ Simple E-Commerce App</h1>
      <div style={{ display: 'flex', gap: '40px' }}>
        
        {/* Product List Section - Uses the ProductList container */}
        <section style={{ flex: 2 }}>
          <h2>Products</h2>
          <ProductList 
            products={products} 
            onAddToCart={addToCart} // <-- Passing the function down
          />
        </section>

        {/* Cart Section */}
        <section style={{ flex: 1, borderLeft: '1px solid #ccc', paddingLeft: '20px' }}>
          <h2>Your Cart</h2>
          <Cart
            items={cartItems}
            total={cartTotal}
            onRemoveFromCart={removeFromCart}
          />
        </section>
      </div>
    </div>
  );
}

export default App;

βœ… Benefits of Component Separation

 

By splitting the product display into two components, we achieve better React practices:

  • ProductList: Responsible for listing (the structure and mapping).

  • ProductCard: Responsible for presenting a single item’s details and handling its specific action (the button).

This makes it easy to change the appearance of a product card without touching the listing logic, and vice versa.

Useful links below:

Let me & my team build you a money making website/blog for your businessΒ https://bit.ly/tnrwebsite_service

Get Bluehost hosting for as little as $1.99/month (save 75%)…https://bit.ly/3C1fZd2

Best email marketing automation solution on the market!Β http://www.aweber.com/?373860

Build high converting sales funnels with a few simple clicks of your mouse!Β https://bit.ly/484YV29

Join my Patreon for one-on-one coaching and help with your coding…https://www.patreon.com/c/TyronneRatcliff

Buy me a coffeeΒ β˜•οΈhttps://buymeacoffee.com/tyronneratcliff

{ 0 comments… add one }

Leave a Comment