Get selected item and its count

岁酱吖の 提交于 2021-02-16 15:13:12

问题


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

  1. You've multiple AppProvider components each providing a different context to their wrapped children.
  2. The initial AppContext shape doesn't match what is actually passed as the context's value.
  3. The cartItems state isn't maintained as an array.
  4. Other various issues and patterns

Solution

  1. 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>
        );
      }
    }
    
  2. Fix the context to have an initial value that matches the provided value. Fix the state updaters to correctly manage the cartItems array. Use an useEffect hook to compute the derived total item count state when the cartItems 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>
      );
    };
    
  3. 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>
      );
    };
    
  4. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!