Overview
The shopping cart is implemented using React Context API for global state management. It provides cart operations, automatic price calculations, and persistent state across the application.
Architecture
Context Provider
CarritoContext wraps the entire app and provides cart state
Custom Hook
useCarrito hook provides access to cart operations
Cart Components
Specialized components for displaying and managing cart items
Context Implementation
CarritoContext Provider
src/context/CarritoContext.jsx
import { createContext , useContext , useState } from "react" ;
const CarritoContext = createContext ();
export const CarritoProvider = ({ children }) => {
const [ items , setItems ] = useState ([]);
const agregarAlCarrito = ( producto ) => {
setItems ( prev => {
const existe = prev . find ( i => i . id === producto . id );
if ( existe ) {
return prev . map ( i =>
i . id === producto . id ? { ... i , cantidad: i . cantidad + 1 } : i
);
}
return [ ... prev , { ... producto , cantidad: 1 }];
});
};
const updateCantidad = ( id , cantidad ) => {
if ( cantidad < 1 ) return ;
setItems ( prev => prev . map ( i => i . id === id ? { ... i , cantidad } : i ));
};
const removeItem = ( id ) =>
setItems ( prev => prev . filter ( i => i . id !== id ));
const subtotal = items . reduce (( sum , i ) => sum + i . precio * i . cantidad , 0 );
const envio = subtotal > 5000 ? 0 : 800 ;
const total = subtotal + envio ;
return (
< CarritoContext.Provider value = { {
items ,
agregarAlCarrito ,
updateCantidad ,
removeItem ,
subtotal ,
envio ,
total ,
} } >
{ children }
</ CarritoContext.Provider >
);
};
export const useCarrito = () => useContext ( CarritoContext );
export default useCarrito ;
src/context/CarritoContext.jsx
State Structure
Cart Item Object
interface CartItem {
id : number ; // Product ID
nombre : string ; // Product name
precio : number ; // Unit price in ARS
img ?: string ; // Product image URL
talle ?: string ; // Size (optional)
cantidad : number ; // Quantity in cart
}
Context Value
Array of items currently in the cart
Add a product to the cart or increment quantity if it exists ( producto : Product ) => void
Update the quantity of a cart item ( id : number , cantidad : number ) => void
Remove an item from the cart
Sum of all items (price × quantity)
Shipping cost (800 ARS or free if subtotal > 5000)
Final total (subtotal + shipping)
Cart Operations
Adding Items
When adding a product to the cart:
Check if the product already exists in the cart
If it exists, increment the quantity by 1
If it’s new, add it with quantity 1
const agregarAlCarrito = ( producto ) => {
setItems ( prev => {
const existe = prev . find ( i => i . id === producto . id );
if ( existe ) {
// Increment existing item
return prev . map ( i =>
i . id === producto . id ? { ... i , cantidad: i . cantidad + 1 } : i
);
}
// Add new item
return [ ... prev , { ... producto , cantidad: 1 }];
});
};
Products with the same ID are treated as the same item, even if they have different attributes. The quantity is simply incremented.
Updating Quantity
Update the quantity of a specific item:
const updateCantidad = ( id , cantidad ) => {
if ( cantidad < 1 ) return ; // Prevent quantity < 1
setItems ( prev => prev . map ( i => i . id === id ? { ... i , cantidad } : i ));
};
The minimum quantity is 1. To remove an item, use removeItem instead of setting quantity to 0.
Removing Items
Remove an item completely from the cart:
const removeItem = ( id ) =>
setItems ( prev => prev . filter ( i => i . id !== id ));
Price Calculations
Subtotal
Sum of all items (unit price × quantity):
const subtotal = items . reduce (( sum , i ) => sum + i . precio * i . cantidad , 0 );
Shipping Cost
Free shipping for orders over 5000 ARS:
const envio = subtotal > 5000 ? 0 : 800 ;
Standard Shipping 800 ARS for orders under 5000 ARS
Free Shipping Free for orders 5000 ARS and above
Total
Final amount including shipping:
const total = subtotal + envio ;
Using the Cart Hook
In Components
import useCarrito from "../../hooks/useCarrito" ;
function ProductCard ({ producto }) {
const { agregarAlCarrito } = useCarrito ();
const handleAdd = () => {
agregarAlCarrito ({
id: producto . idProducto ,
nombre: producto . nombre ,
precio: producto . precio ,
img: producto . img ,
talle: producto . talle ?? null ,
});
};
return (
< button onClick = { handleAdd } >
Agregar al carrito
</ button >
);
}
In Cart Page
src/pages/Cart/Carrito.jsx
import useCarrito from "../../hooks/useCarrito" ;
import CarritoItem from "../../components/Carrito/CarritoItem" ;
import CarritoResumen from "../../components/Carrito/CarritoResumen" ;
import CarritoVacio from "../../components/Carrito/CarritoVacio" ;
export const Carrito = () => {
const { items , updateCantidad , removeItem , subtotal , envio , total } = useCarrito ();
if ( items . length === 0 ) return < CarritoVacio /> ;
return (
< div >
< h1 > Mi Carrito </ h1 >
< p > { items . length } producto { items . length !== 1 ? "s" : "" } en tu carrito </ p >
< div style = { { display: "grid" , gridTemplateColumns: "1fr 360px" } } >
{ /* Lista productos */ }
< div >
{ items . map ( item => (
< CarritoItem
key = { item . id }
item = { item }
updateCantidad = { updateCantidad }
removeItem = { removeItem }
/>
)) }
</ div >
{ /* Resumen */ }
< CarritoResumen subtotal = { subtotal } envio = { envio } total = { total } />
</ div >
</ div >
);
};
src/pages/Cart/Carrito.jsx
Cart Components
CarritoItem
Displays a single cart item with controls:
Product image and name
Unit price
Quantity controls (+/-)
Line total (price × quantity)
Remove button
See Components for implementation details.
CarritoResumen
Displays order summary:
function CarritoResumen ({ subtotal , envio , total }) {
return (
< div className = "summary-panel" >
< h3 > Resumen del pedido </ h3 >
< div className = "summary-row" >
< span > Subtotal </ span >
< span > $ { subtotal . toLocaleString ( "es-AR" ) } </ span >
</ div >
< div className = "summary-row" >
< span > Envío </ span >
< span >
{ envio === 0
? "Gratis"
: `$ ${ envio . toLocaleString ( "es-AR" ) } `
}
</ span >
</ div >
< div className = "summary-row total" >
< span > Total </ span >
< span > $ { total . toLocaleString ( "es-AR" ) } </ span >
</ div >
< button > Finalizar compra </ button >
</ div >
);
}
CarritoVacio
Empty state component when cart has no items:
function CarritoVacio () {
return (
< div className = "empty-cart" >
< span style = { { fontSize: "4rem" } } > 🛒 </ span >
< h2 > Tu carrito está vacío </ h2 >
< p > Agrega productos para comenzar tu compra </ p >
< Link to = "/" >
< button > Ver productos </ button >
</ Link >
</ div >
);
}
State Management Flow
Hook Export Pattern
The useCarrito hook is exported in two ways:
src/context/CarritoContext.jsx
// Named export
export const useCarrito = () => useContext ( CarritoContext );
// Default export
export default useCarrito ;
And re-exported from a dedicated hooks file:
export { useCarrito as default } from "../context/CarritoContext" ;
This allows both import styles:
// Default import
import useCarrito from "../../hooks/useCarrito" ;
// Named import
import { useCarrito } from "../../context/CarritoContext" ;
Future Enhancements
LocalStorage Persistence Save cart state to localStorage to persist across page refreshes
Cart Badge Counter Show total item count in the header cart icon
Product Variants Support different sizes/colors as separate cart items
Discount Codes Apply promotional codes to the cart total
Adding LocalStorage Persistence
Example implementation for persisting cart state:
import { createContext , useContext , useState , useEffect } from "react" ;
const CART_STORAGE_KEY = "huellitas_cart" ;
export const CarritoProvider = ({ children }) => {
// Initialize from localStorage
const [ items , setItems ] = useState (() => {
const saved = localStorage . getItem ( CART_STORAGE_KEY );
return saved ? JSON . parse ( saved ) : [];
});
// Save to localStorage on every change
useEffect (() => {
localStorage . setItem ( CART_STORAGE_KEY , JSON . stringify ( items ));
}, [ items ]);
// ... rest of implementation
};
Be careful with localStorage in SSR environments. Always check if window is defined before accessing localStorage.
Testing the Cart
Manual Testing Checklist
Add items
Add multiple products and verify quantity increments for duplicates
Update quantities
Use +/- buttons to change quantities and verify price updates
Remove items
Delete items and verify they’re removed from the list
Check calculations
Verify subtotal, shipping (free over 5000), and total are correct
Empty cart
Remove all items and verify empty state is shown
Next Steps
Components Learn about cart UI components
Backend API Connect cart to backend checkout