Posts

Tienda virtual - Crea un carrito de compras con React.js

avatar of @jfdesousa7
25
@jfdesousa7
·
0 views
·
4 min read


Hoy vamos a crear un carrito de compras simple de compras con React.js vamos a consumir la API de Fakestoreapi.com además usaremos material-ui que nos proveerá de Componentes de estilos para darle forma a nuestra Aplicación web, ademas aprenderemos a usar react-query para obtener los datos de la API



Crearemos una carpeta en el escritorio de nombre shopping-cart/

Una vez creado la carpeta navegamos hacia ella y ejecutamos


 
npx create-react-app . 
 

Instalaremos los siguientes modulos:

 
npm i react-query @material/core @material/icons 
 

Es momento de codear

index.js

 
import React from "react"; 
import ReactDOM from "react-dom"; 
import { QueryClient, QueryClientProvider } from "react-query"; 
import App from "./App"; 
const client = new QueryClient(); 
ReactDOM.render( 
  <QueryClientProvider client={client}> 
    <App /> 
  </QueryClientProvider>, 
  document.getElementById("root") 
); 
 

App.js

 
import { useState } from "react"; 
import { useQuery } from "react-query"; 
//Components 
import Item from "./components/Item"; 
import CartList from "./components/CartList"; 
import Navbar from "./components/Navbar"; 
import Drawer from "@material-ui/core/Drawer"; 
import LinearProgress from "@material-ui/core/LinearProgress"; 
import Grid from "@material-ui/core/Grid"; 
import "./custom.css"; 
const getProducts = async () => 
  await (await fetch("https://fakestoreapi.com/products/")).json(); 
const App = () => { 
  const { isLoading, error, data } = useQuery("products", getProducts); 
  const [cartOpen, setCartOpen] = useState(false); 
  const [cartItems, setCartItems] = useState([]); 
  const getTotalItems = (cartItems) => 
    cartItems.reduce((acum, i) => acum + i.amount, 0); 
  const handleAddItemToCart = (item) => { 
    setCartItems((prev) => { 
      // Search the item in the array 
      const isItemInTheCart = prev.find((i) => i.id === item.id); 
      if (isItemInTheCart) { 
        return prev.map((i) => 
          i.id === item.id ? { ...i, amount: i.amount + 1 } : i 
        ); 
      } 
      return [...prev, { ...item, amount: 1 }]; 
    }); 
  }; 
  const handleRemoveItemFromCart = (id) => { 
    setCartItems((prev) => { 
      const foundItem = prev.find((i) => i.id === id); 
      if (foundItem) { 
        if (foundItem.amount === 1) { 
          const newArray = prev.filter((i) => i.id !== id); 
          return newArray; 
        } else { 
          return prev.map((i) => 
            i.id === id ? { ...i, amount: i.amount - 1 } : i 
          ); 
        } 
      } else { 
        return prev; 
      } 
    }); 
  }; 
  if (isLoading) return <LinearProgress>; 
  if (error) return error.message; 
  return ( 
    <> 
      <Navbar 
        getTotalItems={getTotalItems(cartItems)} 
        setCartOpen={setCartOpen} 
      ></Navbar> 
      <div className="main"> 
        <Drawer 
          anchor="right" 
          open={cartOpen} 
          onClose={() => setCartOpen(false)} 
        > 
          <CartList 
            cartItems={cartItems} 
            handleAddItemToCart={handleAddItemToCart} 
            handleRemoveItemFromCart={handleRemoveItemFromCart} 
          /> 
        </Drawer> 
        <Grid container spacing={3}> 
          {data?.map((item) => ( 
            <Grid key={item.id} item xs={12} sm={4}> 
              <Item item={item} handleAddItemToCart={handleAddItemToCart} /> 
            </Grid> 
          ))} 
        </Grid> 
      </div> 
    </> 
  ); 
}; 
export default App; 
 

Creamos un archivo custom de css

custom.css

 
@import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@300&display=swap'); 
body { 
    margin :0; 
    padding:0; 
    font-family: 'Quicksand', sans-serif; 
} 
.main { 
    padding:40px 
} 
.cartIcon { 
    position: fixed; 
    top: 10px; 
    right: 140px; 
    cursor:pointer; 
    z-index: 100; 
    background: yellow; 
} 
aside { 
    width:460px; 
    margin : 20px; 
} 
.itemCart { 
    display:flex; 
    justify-content: space-between; 
} 
.itemCart img{ 
    max-width: 80px; 
    object-fit: cover; 
    margin-left: 40px; 
} 
.itemCart div{ 
    flex : 1 
} 
.itemInfo { 
    display: flex; 
    justify-content : space-between 
} 
.buttons { 
    display:flex; 
    justify-content : space-between 
} 
 

Componentes

components/Navbar.js

 
import React from "react"; 
import { makeStyles } from "@material-ui/core/styles"; 
import AppBar from "@material-ui/core/AppBar"; 
import Toolbar from "@material-ui/core/Toolbar"; 
import IconButton from "@material-ui/core/IconButton"; 
import Badge from "@material-ui/core/Badge"; 
import Menu from "@material-ui/core/Menu"; 
import AddShoppingCart from "@material-ui/icons/AddShoppingCart"; 
const useStyles = makeStyles(() => ({ 
  grow: { 
    flexGrow: 1, 
  } 
})); 
export default function Navbar({ getTotalItems, setCartOpen }) { 
  const classes = useStyles(); 
  const [anchorEl, setAnchorEl] = React.useState(null); 
  const [mobileMoreAnchorEl, setMobileMoreAnchorEl] = React.useState(null); 
  const isMenuOpen = Boolean(anchorEl); 
  const isMobileMenuOpen = Boolean(mobileMoreAnchorEl); 
  const handleMobileMenuClose = () => { 
    setMobileMoreAnchorEl(null); 
  }; 
  const handleMenuClose = () => { 
    setAnchorEl(null); 
    handleMobileMenuClose(); 
  }; 
  const menuId = "primary-search-account-menu"; 
  const renderMenu = ( 
    <Menu 
      anchorEl={anchorEl} 
      anchorOrigin={{ vertical: "top", horizontal: "right" }} 
      id={menuId} 
      keepMounted 
      transformOrigin={{ vertical: "top", horizontal: "right" }} 
      open={isMenuOpen} 
      onClose={handleMenuClose} 
    ></Menu> 
  ); 
  const mobileMenuId = "primary-search-account-menu-mobile"; 
  const renderMobileMenu = ( 
    <Menu 
      anchorEl={mobileMoreAnchorEl} 
      anchorOrigin={{ vertical: "top", horizontal: "right" }} 
      id={mobileMenuId} 
      keepMounted 
      transformOrigin={{ vertical: "top", horizontal: "right" }} 
      open={isMobileMenuOpen} 
      onClose={handleMobileMenuClose} 
    ></Menu> 
  ); 
  return ( 
    <div className={classes.grow}> 
      <AppBar position="static"> 
        <Toolbar> 
          <b>TU TIENDA ONLINE</b>   |   <a target="_blank" style={{color:'#fff'}} href='https://hive.blog/@jfdesousa7'>Jfdesousa7</a> 
          <div className={classes.grow} /> 
          <div onClick={() => setCartOpen(true)} 
          > 
            <IconButton aria-label="show 4 new mails" color="inherit"> 
              <Badge badgeContent={getTotalItems} color="secondary"> 
                <AddShoppingCart /> 
              </Badge> 
            </IconButton> 
          </div> 
        </Toolbar> 
      </AppBar> 
      {renderMobileMenu} 
      {renderMenu} 
    </div> 
  ); 
} 
 

components/Item.js

 
import Card from "@material-ui/core/Card"; 
import { makeStyles } from '@material-ui/core/styles'; 
import CardActionArea from "@material-ui/core/CardActionArea"; 
import CardActions from "@material-ui/core/CardActions"; 
import CardContent from "@material-ui/core/CardContent"; 
import CardMedia from "@material-ui/core/CardMedia"; 
import Button from "@material-ui/core/Button"; 
const useStyles = makeStyles({ 
    root: { 
      maxWidth: 345, 
    }, 
  }); 
export default function Item({item, handleAddItemToCart }) { 
    const classes = useStyles(); 
  return ( 
    <Card className={classes.root}> 
      <CardActionArea> 
        <CardMedia 
          component="img" 
          alt={item.title} 
          height="200" 
          image={item.image} 
          title={item.title} 
        /> 
        <CardContent> 
          {item.description} 
        </CardContent> 
      </CardActionArea> 
      <CardActions> 
      <Button size="small" color="secondary"> 
          $ {item.price} 
        </Button> 
        <Button size="small" color="primary" onClick = { () => handleAddItemToCart(item)}> 
          Add to Cart 
        </Button> 
      </CardActions> 
    </Card> 
  ); 
} 
 

components/CartList.js

 
import CartItem from "./CartItem"; 
const CartList = ({ cartItems, handleAddItemToCart, handleRemoveItemFromCart  }) => { 
  const calculeTotal = cartItems.reduce( 
    (sum, i) => sum + i.amount * i.price, 
    0 
  ); 
  return ( 
    <aside> 
      <h1>Carrito de compras</h1> 
      {cartItems.length === 0 ? <h3>No hay productos agregados al carrito todavia...</h3> : null} 
      <div> 
        {cartItems.map((i) => ( 
          <CartItem item={i} handleAddItemToCart={handleAddItemToCart} handleRemoveItemFromCart={handleRemoveItemFromCart} /> 
        ))} 
      </div> 
      <h2>Total: {calculeTotal.toFixed(2)}</h2> 
    </aside> 
  ); 
}; 
export default CartList; 
 

components/CartItem.js

 
import Button from "@material-ui/core/Button"; 
const CartItem = ({ item, handleAddItemToCart, handleRemoveItemFromCart }) => { 
  return ( 
    <aside> 
      <div className="itemCart"> 
        <div> 
          <h3>{item.title}</h3> 
          <div className="itemInfo"> 
            <p>Precio: ${item.price}</p> 
            <p>Total: ${(item.amount * item.price).toFixed(2)}</p> 
          </div> 
          <div className="buttons"> 
            <Button onClick={ () => handleRemoveItemFromCart(item.id)} size="small" disableElevation variant="contained"> 
              - 
            </Button> 
            <p> {item.amount}

<Button size="small" disableElevation variant="contained" onClick={() => handleAddItemToCart(item)} > + </Button> </div> </div> <img src={item.image} /> </div> </aside> ); }; export default CartItem;

Puedes probar por ti mismo el carrito de compras en el link de abajo

click en el enlace https://shopping-cart-react-jfdesousa7.herokuapp.com/


Y con esos amigos llegamos al final del tutorial, espero que lo hayan disfrutado y ¡hasta la próxima!


Visite mi sitio web oficial para presupuestos y mucho más

TupaginaOnline.net