DEV Community

A0mineTV
A0mineTV

Posted on • Edited on

Build a Shopping Cart App with React, TypeScript, and Material-UI πŸš€

Build a Shopping Cart App with React, TypeScript, and Material-UI

Building a modern shopping cart application is a great way to learn React, TypeScript, and Material-UI. In this article, I'll walk you through a project where I implemented a responsive and dynamic shopping cart using React, TypeScript, Material-UI, and react-query.


Features of the Application

Here’s what this app can do:

  • Display a list of products fetched from an API.
  • Add products to a shopping cart.
  • Update the quantity of items in the cart.
  • Calculate the total price of items in the cart.
  • Use Material-UI components for a modern UI.
  • Handle asynchronous data fetching using react-query.

Project Overview

Tech Stack

  • React: For building the user interface.
  • TypeScript: For type safety and better code maintainability.
  • Material-UI (MUI): For styling and pre-built components.
  • react-query: For efficient data fetching and caching.
  • styled-components: For custom styling.

Folder Structure

The project follows a modular structure:

src/
β”œβ”€β”€ components/
β”‚   β”œβ”€β”€ Cart/
β”‚   β”‚   β”œβ”€β”€ Cart.tsx
β”‚   β”‚   β”œβ”€β”€ Cart.styles.ts
|   β”œβ”€β”€ CartItem/
β”‚   β”‚   β”œβ”€β”€ CartItem.tsx
β”‚   β”‚   β”œβ”€β”€ CartItem.styles.ts
β”‚   β”œβ”€β”€ Item/
β”‚   β”‚   β”œβ”€β”€ Item.tsx
β”‚   β”‚   β”œβ”€β”€ Item.styles.ts
|   | 
β”œβ”€β”€ App.tsx
β”œβ”€β”€ App.styles.ts
β”œβ”€β”€ index.tsx
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Implementation

1. Define Types for Product Data**

TypeScript makes it easy to define the structure of the product data:

export type CardItemType = {
  id: number;
  category: string;
  description: string;
  image: string;
  price: number;
  title: string;
  amount: number;
};
Enter fullscreen mode Exit fullscreen mode

2. Fetch Data with react-query

We fetch product data from FakeStore API using react-query:

const getProducts = async (): Promise<CardItemType[]> =>
  await (await fetch('https://fakestoreapi.com/products')).json();

const { data, isLoading, error } = useQuery<CardItemType[]>('products', getProducts);
Enter fullscreen mode Exit fullscreen mode

3. Build the UI

Product List (Item.tsx)

The Item component displays individual products and includes a button to add the product to the cart.

const Item: React.FC<Props> = ({ item, handleAddToCart }) => (
  <Wrapper>
    <img src={item.image} alt={item.title} />
    <div>
      <h3>{item.title}</h3>
      <p>{item.description}</p>
      <h3>${item.price}</h3>
    </div>
    <Button onClick={() => handleAddToCart(item)}>Add to cart</Button>
  </Wrapper>
);
Enter fullscreen mode Exit fullscreen mode

**Shopping Cart (Cart.tsx)

The Cart component displays items in the cart and calculates the total price.

const Cart: React.FC<Props> = ({ cartItems, addToCart, removeFromCart }) => {
  const calculateTotal = (items: CardItemType[]) =>
    items.reduce((ack: number, item) => ack + item.amount * item.price, 0);

  return (
    <Wrapper>
      <h2>Your Shopping Cart</h2>
      {cartItems.length === 0 ? <p>No items in cart.</p> : null}
      {cartItems.map((item) => (
        <CartItem
          key={item.id}
          item={item}
          addToCart={addToCart}
          removeFromCart={removeFromCart}
        />
      ))}
      <h2>Total: ${calculateTotal(cartItems).toFixed(2)}</h2>
    </Wrapper>
  );
};
Enter fullscreen mode Exit fullscreen mode

4. Add Styling

We used styled-components for custom styles. For example, here’s the Wrapper for the Itemcomponent:

export const Wrapper = styled.div`
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  border: 1px solid lightblue;
  border-radius: 10px;
  height: 100%;
  overflow: hidden;

  img {
    max-height: 250px;
    object-fit: cover;
  }

  button {
    background-color: #1976d2;
    color: white;
    font-weight: bold;

    &:hover {
      background-color: #1565c0;
    }
  }
`;
Enter fullscreen mode Exit fullscreen mode

5. Manage Cart State

We used useStateto manage the cart state in App.tsx:

const [cartItems, setCartItems] = useState<CardItemType[]>([]);

const handleAddToCart = (clickedItem: CardItemType) => {
  setCartItems((prevState) => {
    const isItemInCart = prevState.find((item) => item.id === clickedItem.id);
    if (isItemInCart) {
      return prevState.map((item) =>
        item.id === clickedItem.id ? { ...item, amount: item.amount + 1 } : item
      );
    }
    return [...prevState, { ...clickedItem, amount: 1 }];
  });
};

const handleRemoveFromCart = (id: number) => {
  setCartItems((prevState) =>
    prevState.reduce((ack, item) => {
      if (item.id === id) {
        if (item.amount === 1) return ack;
        return [...ack, { ...item, amount: item.amount - 1 }];
      } else {
        return [...ack, item];
      }
    }, [] as CardItemType[])
  );
};
Enter fullscreen mode Exit fullscreen mode

Conclusion

This project demonstrates how to create a fully functional shopping cart using modern React and TypeScript. You can enhance it further by adding features like user authentication, payment integration, or state management libraries like Redux or Zustand.

Check out the full code on GitHub.

Top comments (2)

Collapse
 
stevendev0822 profile image
Steven

Good article.
Thanks for sharing.
But it would be better if you could style the format of folder structure so that the others can easily understand it.

Collapse
 
blamsa0mine profile image
A0mineTV

Hello, thank you for your feedback.
I've changed the folder structure, and it's much easier to read.