I am trying to create a cart page. I have a product file that fetches products from fakestoreapi, maps through them and displays it. There is also a button that onClick passes product(a single product object retrieved from the fetched data) to another component (CarItems) and creates divs containing cart items.
My problem is I seem to only be able to display it on the product file page rather than the cart file that I want.
I've tried separating the CartItems component to another file but that seems to stop any variable from passing to it. Tried using propTypes from on tutorial but that also didn't help. Any solution would only allow me to display the items of the cart in my product page file.
Product.jsx
import { useState } from 'react'
import { useEffect } from 'react'
import './Products.css'
import NavBar from './NavBar.jsx'
function CartItems({ products }) {
const [quantity, setQuantity] = useState(0);
function add() {
setQuantity(quantity + 1);
}
function subtract() {
if (quantity > 0) {
setQuantity(quantity - 1);
}
}
if (products && products.length === 0) {
console.log("Empty Cart")
return (
<>
<h2>Cart is Empty</h2>
</>
)
} else {
console.log("Car has items")
return (
<div id="cartItemList">
{products && products.map(product => (
<div className="cartItem" key={product.id}>
<img src={product.image} alt={product.title} />
<p>{product.title}</p>
<p>{product.description}</p>
<p>${product.price}</p>
<div>
<button onClick={subtract}>-</button>
<p>{quantity}</p>
<button onClick={add}>+</button>
</div>
</div>
))}
</div>
)
}
}
function Products() {
const [data, setData] = useState([]);
const [cartProducts, setCartProducts] = useState([]);
useEffect(() => {
fetch('https://fakestoreapi.com/products?limit=4')
.then(response => response.json())
.then((data) => setData(data))
.catch((error) => console.error(error));
}, []);
function addToCart(product) {
setCartProducts(prevCart => [...prevCart, product]);
}
return (
<>
<NavBar />
<div id="productsContainer">
{data.map(product => (
<div className="productBoxes" key={product.id}>
<img className="productImg" src={product.image} alt={product.title} />
<h1>{product.title}</h1>
<p>{product.description}</p>
<p>${product.price}</p>
<button onClick={() => addToCart(product)} className="productCheck" id="productButton">Add to Cart</button>
</div>
))}
<CartItems products={cartProducts} />
</div>
</>
);
}
export { Products, CartItems };
Cart.jsx
// import { useState } from 'react'
import './Cart.css'
import NavBar from './NavBar.jsx'
import { CartItems } from './Products.jsx'
function Cart() {
return (
<>
<NavBar />
<CartItems />
</>
);
}
export default Cart;
App.jsx
import NavBar from './NavBar.jsx'
import './App.css'
function App() {
return (
<>
<NavBar />
<div id="hero">
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Sapiente, ullam quaerat. Dolores labore aspernatur eos, molestiae hic laboriosam, magni nostrum vel aut mollitia voluptatum ducimus nesciunt minima, voluptas ea explicabo.</p>
<a href="products"><button>Products</button></a>
</div>
<footer>
<p>Made by Christian A. Valeri</p>
</footer>
</>
)
}
export default App;
Main.jsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import App from './App.jsx'
import Cart from './Cart.jsx'
import {CartItems, Products} from './Products.jsx'
const router = createBrowserRouter([
{
path: "/",
element: <App />,
},
{
path: "/App",
element: <App />,
},
{
path: "/products",
element: <Products />,
},
{
path: "/cart",
element: <Cart />,
},
]);
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
);
Right now, only 2 ways come to my mind for achieving what you want. The first is using the useParams hook provided by react-router-dom, and the second would be to use any state management library like redux or React's in-built Context API with useState or useReducer.
I opted in for Context API + useState hook because it'd be easier for you to understand and it is very scalable. Here's the solution:
main.jsx
import React from "react";
import ReactDOM from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import App from "./App";
import Cart from "./components/Cart";
import Products from "./components/Products";
import ProductContextProvider from "./context/ProductContext";
const router = createBrowserRouter([
{
path: "/",
element: <App />,
},
{
path: "/App",
element: <App />,
},
{
path: "/products",
element: <Products />,
},
{
path: "/cart",
element: <Cart />,
},
]);
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<ProductContextProvider>
<RouterProvider router={router} />
</ProductContextProvider>
</React.StrictMode>
);
App.jsx
import { Link } from "react-router-dom";
function App() {
return (
<>
<div id="hero">
<p>
Lorem ipsum dolor sit amet consectetur adipisicing elit. Sapiente,
ullam quaerat. Dolores labore aspernatur eos, molestiae hic
laboriosam, magni nostrum vel aut mollitia voluptatum ducimus nesciunt
minima, voluptas ea explicabo.
</p>
<Link to="/products">
<button>Products</button>
</Link>
</div>
<footer>
<p>Made by Christian A. Valeri</p>
</footer>
</>
);
}
export default App;
The following files are located in a folder named "components":
Cart.jsx
import { useProductContext } from "../context/ProductContext";
import CartItems from "./CartItems";
function Cart() {
const { cartProducts } = useProductContext();
return <CartItems products={cartProducts} />;
}
export default Cart;
CartItems.jsx
import { useState } from "react";
function CartItems({ products }) {
const [quantity, setQuantity] = useState(0);
function add() {
setQuantity(quantity + 1);
}
function subtract() {
if (quantity > 0) {
setQuantity(quantity - 1);
}
}
if (products && products.length === 0) {
console.log("Empty Cart");
return (
<>
<h2>Cart is Empty</h2>
</>
);
} else {
console.log("Cart has items");
return (
<div id="cartItemList">
{products &&
products.map((product) => (
<div className="cartItem" key={product.id}>
<img src={product.image} alt={product.title} />
<p>{product.title}</p>
<p>{product.description}</p>
<p>${product.price}</p>
<div>
<button onClick={subtract}>-</button>
<p>{quantity}</p>
<button onClick={add}>+</button>
</div>
</div>
))}
</div>
);
}
}
export default CartItems;
Products.jsx
import { useState } from "react";
import { useEffect } from "react";
import { useProductContext } from "../context/ProductContext";
import { Link } from "react-router-dom";
function Products() {
const [data, setData] = useState([]);
const { addToCart } = useProductContext();
useEffect(() => {
fetch("https://fakestoreapi.com/products?limit=4")
.then((response) => response.json())
.then((data) => setData(data))
.catch((error) => console.error(error));
}, []);
return (
<div id="productsContainer">
{data.map((product) => (
<div className="productBoxes" key={product.id}>
<img className="productImg" src={product.image} alt={product.title} />
<h1>{product.title}</h1>
<p>{product.description}</p>
<p>${product.price}</p>
<button
onClick={() => {
addToCart(product);
alert("Product added to cart");
}}
className="productCheck"
id="productButton"
>
Add to Cart
</button>
</div>
))}
<br />
<br />
<br />
<br />
<br />
<Link to="/cart">
<button>Checkout</button>
</Link>
</div>
);
}
export default Products;
This file below is stored in a folder called "context":
ProductContext.jsx
import { createContext, useContext, useState } from "react";
const ProductContext = createContext([]);
function ProductContextProvider({ children }) {
const [cartProducts, setCartProducts] = useState([]);
const addToCart = (product) => {
setCartProducts((prevCart) => [...prevCart, product]);
};
return (
<ProductContext.Provider value={{ cartProducts, addToCart }}>
{children}
</ProductContext.Provider>
);
}
export default ProductContextProvider;
function useProductContext() {
return useContext(ProductContext);
}
export { useProductContext };
Try this and get back to me if you have any further questions
#react #context #react-hooks #javascript