问题
I am trying to make a shopping cart app like this site but using reactjs.
index.js: (Sending each product to product component
)
{products.length > 0
? products.map((product) => (
<Product key={product.id} product={product} />
))
: ""}
components/product.js:
<div>
{product?.price}
<h3>
{product.name ? product.name : ""}
</h3>
<div dangerouslySetInnerHTML={{ __html: product?.description }} />
</div>
Also I have the ADD button UI switch code and that will look like,
Before clicking add button,
------------
| ADD +|
------------
After clicking add button,
-----------------
| - {count} +|
-----------------
Things are fine as of now and the respective count(s) on each product individually gets added in home page.
Also with the help of contextApi made an AppContext
and updated the cart like,
const addToCart = (product) => {
if (+cart >= 0) {
setCart(+cart + 1);
setCartItems(product);
}
};
In nav.js, I am getting the updated count of the products but when I click on the bag menu (Which is a cart page) , I am unable to fetch the context.
Expected Result
While visiting the cart page (On click of the bag menu in header), the page would display the so far selected product (with name and description) along with their quantity respectively.
Current Result:
Unable to get the appcontext data and display the selected product along with the quantity.
Working Snippet:
Please kindly help me to achieve the result of displaying the selected product and its respective quantity while navigating to cart (bag) page.. I am struggling with this for long.. So I humbly request you to help me with right solution.. Big thanks in advance..
回答1:
Issues
- You've multiple
AppProvider
components each providing a different context to their wrapped children. - The initial
AppContext
shape doesn't match what is actually passed as the context's value. - The
cartItems
state isn't maintained as an array. - Other various issues and patterns
Solution
Remove all the extraneous
AppProvider
provider components, use just one to wrap your entire app in_app.js
.import { AppProvider } from "../components/context/AppContext"; export default class TailwindApp extends App { render() { const { Component, pageProps } = this.props; return ( <AppProvider> <Component {...pageProps} /> </AppProvider> ); } }
Fix the context to have an initial value that matches the provided value. Fix the state updaters to correctly manage the
cartItems
array. Use anuseEffect
hook to compute the derived total item count state when thecartItems
array state updates. Correctly add and update item/product quantities, and remove item when quantity reaches 0.import React, { useState, useEffect } from "react"; export const AppContext = React.createContext({ cart: 0, cartItems: [], setCart: () => {}, addToCart: () => {}, removeFromCart: () => {} }); export const AppProvider = (props) => { const [cart, setCart] = useState(0); const [cartItems, setCartItems] = useState([]); useEffect(() => { setCart(cartItems.reduce((count, { quantity }) => count + quantity, 0)); }, [cartItems]); const addToCart = (product) => { setCartItems((items) => { if (items.find(({ id }) => id === product.id)) { return items.map((item) => item.id === product.id ? { ...item, quantity: item.quantity + 1 } : item ); } else { return [ ...items, { ...product, quantity: 1 } ]; } }); }; const removeFromCart = (product) => { setCartItems((items) => { const foundItem = items.find(({ id }) => id === product.id); if (foundItem?.quantity > 1) { return items.map((item) => item.id === product.id ? { ...item, quantity: item.quantity - 1 } : item ); } else { return items.filter(({ id }) => id !== product.id); } }); }; return ( <AppContext.Provider value={{ cart, addToCart, removeFromCart, cartItems }}> {props.children} </AppContext.Provider> ); };
In
Products.js
remove all the local item count state, quantities can be accessed from state in the context. Conditionally render the "Add Item" and increment/decrement buttons on the item count.import Link from "next/link"; import { useContext } from "react"; import { AppContext } from "./context/AppContext"; const Product = (props) => { const { product } = props; const contextData = useContext(AppContext); const count = contextData.cartItems.find(({ id }) => id === product.id)?.quantity ?? 0; const addToCart = (product) => () => { contextData.addToCart(product); }; const removeFromCart = (product) => () => { contextData.removeFromCart(product); }; return ( ... {count ? ( ... {count} ... ) : ( ... <span className="label">ADD</span> ... )} </div> </div> ); };
In
Cart.js
you can import and consume the context to render and display the cart items.import { useContext } from "react"; import { AppContext } from "../components/context/AppContext"; const Cart = () => { const { cart, cartItems } = useContext(AppContext); return ( <div> <h1> Cart Page </h1> <h2>Total Item Count: {cart}</h2> <p> <ul> {cartItems.map(({ id, name, quantity }) => ( <li key={id}> {name}: {quantity} </li> ))} </ul> </p> </div> ); };
Note: Please note that an id
property was added to all items/products in your products
array to make matching/identifying them a much easier task.
Demo
回答2:
You need to move your AppProvider
to _app.js
and remove it from both index.js
and cart.js
then both pages will be able to access the same context.
Your _app.js
should look like this
import React from "react";
import App from "next/app";
import { AppProvider } from "../components/context/AppContext";
export default class TailwindApp extends App {
render() {
const { Component, pageProps } = this.props;
return (
<AppProvider>
<Component {...pageProps} />
</AppProvider>
);
}
}
来源:https://stackoverflow.com/questions/66141345/get-selected-item-and-its-count