E-COMMERCE - Create a shopping cart with React.js
hive-169321·@jfdesousa7·
0.000 HBDE-COMMERCE - Create a shopping cart with React.js
<div id="contentMain">  <table> <tr> <td>Today we are going to create a simple shopping cart with React.js we are going to consume the API of <a href="https://fakestoreapi.com/products/ "> Fakestoreapi.com </a> we will also use <a href = "https://material-ui.com/es/"> material-ui </a> that will provide us with Style Components to shape our Web Application, we will also learn to use <a href="https: / /react-query.tanstack.com/ "> react-query </a> to get the API data </td> </tr> </table> <h1></h1> <p><br><br> <p> We will create a folder on the desktop named <code> shopping-cart </code> / </p> <p> Once the folder is created, we navigate to it and execute </p> <div><b></b><br/></div> <div class="gatsby-highlight"> <pre class="gatsby-code"> <code class="gatsby-code">npx create-react-app . </code> </pre> </div> <br> <p> We will install the following modules: </p> <div class="gatsby-highlight"> <pre class="gatsby-code"> <code class="gatsby-code">npm i react-query @material/core @material/icons </code> </pre> </div> <br> <p><b>index.js</b></p> <div class="gatsby-highlight"> <pre class="gatsby-code"> <code class="gatsby-code">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") ); </code> </pre> </div> <br> <p><b>App.js</b></p> <div class="gatsby-highlight"> <pre class="gatsby-code"> <code class="gatsby-code">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></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; </code> </pre> </div> <br> <p> <b> We create a custom css file </b> </p> <p><b>custom.css</b></p> <div class="gatsby-highlight"> <pre class="gatsby-code"> <code class="gatsby-code">@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 } </code> </pre> </div> <br> <h3>Components</h3> <p><b>components/Navbar.js</b></p> <div class="gatsby-highlight"> <pre class="gatsby-code"> <code class="gatsby-code">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>Shopping Cart</b> | <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> ); } </code> </pre> </div> <br> <p><b>components/Item.js</b></p> <div class="gatsby-highlight"> <pre class="gatsby-code"> <code class="gatsby-code">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> ); } </code> </pre> </div> <br> <p><b>components/CartList.js</b></p> <div class="gatsby-highlight"> <pre class="gatsby-code"> <code class="gatsby-code">import CartItem from "./CartItem"; const CartList = ({ cartItems, handleAddItemToCart, handleRemoveItemFromCart }) => { const calculeTotal = cartItems.reduce( (sum, i) => sum + i.amount * i.price, 0 ); return ( <aside> <h1>Cart</h1> {cartItems.length === 0 ? <h3>No products yet...</h3> : null} <div> {cartItems.map((i) => ( <CartItem item={i} handleAddItemToCart={handleAddItemToCart} handleRemoveItemFromCart={handleRemoveItemFromCart} /> ))} </div> <h2>Total: {calculeTotal.toFixed(2)}</h2> </aside> ); }; export default CartList; </code> </pre> </div> <br> <p><b>components/CartItem.js</b></p> <div class="gatsby-highlight"> <pre class="gatsby-code"> <code class="gatsby-code">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}</p> <Button size="small" disableElevation variant="contained" onClick={() => handleAddItemToCart(item)} > + </Button> </div> </div> <img src={item.image} /> </div> </aside> ); }; export default CartItem; </code> </pre> </div> <br> <br/> <table><tr><td><h3>And with those friends we reached the end of the tutorial, I hope you enjoyed it and until next time! </h3></td> </tr> </table> <br/> <h4>Visit my official website for budges and much more</h4> <center><a href="https://tupaginaonline.net" target="_blank"><b>TupaginaOnline.net</b></a></center> </div>