React-将上下文传递给SweetAlert弹出窗口

发布于 2025-01-24 15:54:33 字数 11931 浏览 0 评论 0 原文

我的上下文如下:

import React, {createContext, useEffect, useState} from "react";

export const CartContext = createContext();

const CartContextProvider = (props) => {
    const [cart, setCart] = useState(JSON.parse(localStorage.getItem('cart')) || []);

    useEffect(() => {
        localStorage.setItem('cart', JSON.stringify(cart));
    }, [cart]);

    const updateCart = (productId, op) => {
        let updatedCart = [...cart];

        if (updatedCart.find(item => item.id === productId)) {
            let objIndex = updatedCart.findIndex((item => item.id === productId));

            if (op === '-' && updatedCart[objIndex].qty > 1) {
                updatedCart[objIndex].qty -= 1;
            } else if (op === '+') {
                updatedCart[objIndex].qty += 1;
            }
        } else {
            updatedCart.push({id: productId, qty: 1})
        }

        setCart(updatedCart);
    }

    const removeItem = (id) => {
        setCart(cart.filter(item => item.id !== id));
    };

    return (
        <CartContext.Provider value={{cart, updateCart, removeItem}}>
            {props.children}
        </CartContext.Provider>
    )
};

export default CartContextProvider;

app.js

import React from "react";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import NavigationBar from "./components/layout/navigationBar/NavigationBar";
import Homepage from "./pages/homepage/Homepage";
import AboutUsPage from "./pages/aboutUs/AboutUsPage";
import ContactPage from "./pages/contact/ContactPage";
import SearchPage from "./pages/search/SearchPage";
import ShoppingCart from "./components/layout/shoppingCart/ShoppingCart";
import CartContextProvider from "./context/CartContext";

function App() {
    return (
        <div>
            <CartContextProvider>
                <Router>
                    <NavigationBar/>
                    <ShoppingCart/>
                    <Routes>
                        <Route exact path="/" element={<Homepage/>}/>
                        <Route path="/a-propos" element={<AboutUsPage/>} />
                        <Route path="/contact" element={<ContactPage/>}/>
                        <Route path="/recherche" element={<SearchPage/>}/>
                    </Routes>
                </Router>
            </CartContextProvider>
        </div>
    );
}

export default App;

在“组件” shoppingcart 中,我正在使用另一个组件 shoppingcartcartquantity ,又使用上下文。它可以根据应有的作用。

这是 shoppingcartquantity 组件:

import React, {useContext} from "react";
import {CartContext} from "../../../context/CartContext";

import styles from './ShoppingCartQuantity.module.css'

const ShoppingCartQuantity = ({productId}) => {
    const {cart, updateCart} = useContext(CartContext);

    let qty = 0;
    if (cart.find((item => item.id === productId))) {
        let objIndex = cart.findIndex((item => item.id === productId));

        qty = cart[objIndex].qty;
    }

    return (
        <div>
            <span>
                <span className={`${styles.op} ${styles.decrementBtn}`} onClick={() => updateCart(productId, '-')}>-</span>
                <span className={styles.qty}>{qty}</span>
                <span className={`${styles.op} ${styles.incrementBtn}`} onClick={() => updateCart(productId, '+')}>+</span>
            </span>
        </div>
    )
}

export default ShoppingCartQuantity;

现在我尝试使用 shoppingcartquantity homepage 组件中的组件是路由元素(请参阅> > app.js )但是获得错误 und offerate typeError:无法破坏属性'of'''(0,react__webpack_imported_module_0 __. usecontext)(...)',因为它是未定义的。

因此,上下文适用于路由器外部的组件,但对其内部的组件不起作用。 访问上下文,或者我缺少某些内容?

所有路由元素是否应该

如果我将路由器包裹在提供商中,那么 >在评论中提出的建议,我尝试在另一个路由元素中使用 shoppingcartquantity 组件,并且效果很好;因此,问题不是路由器!

以下是我如何使用 shoppingcartquantity 组件中的代码 homepage 组件:

import React, { useState, useEffect,  useRef } from "react";
import { Responsive, WidthProvider } from "react-grid-layout";
import Subcat from "../../components/subcat/Subcat";
import CategoryService from "../../services/api/Category";
import SubCategoryService from "../../services/api/SubCategory";
import CategoriesLayout from "../../utils/CategoriesLayout";
import CategoryCard from "../../components/category/CategoryCard";
import { Triangle } from  'react-loader-spinner'
import ScrollIntoView from 'react-scroll-into-view'
import ProductService from "../../services/api/Product";
import Swal from 'sweetalert2'
import withReactContent from 'sweetalert2-react-content';
import YouTube from 'react-youtube';
import FavoriteBtn from "../../components/favorite/FavoriteBtn";
import ShoppingCartQuantity from "../../components/layout/shoppingCart/ShoppingCartQuantity";

import "./Homepage.css";
import "../../components/product/ProductModal.css"
import "react-loader-spinner";
import modalStyles from "../../components/product/ProductModal.module.css"

function Homepage() {
    const [categories, setCategories] = useState([]);
    const [subCats, setSubCats] = useState([]);
    const [loader, setLoader] = useState(false);
    const ResponsiveGridLayout = WidthProvider(Responsive);
    const scrollRef = useRef();
    const productModal = withReactContent(Swal);
    const opts = {
        // height: '390',
        // width: '640',
        playerVars: {
            autoplay: 1,
        }
    };

    useEffect(() => {
        CategoryService.get().then((response) => {
            setCategories(response);
        });
    }, []);

    function showSubCatsHandler(catId) {
        setLoader(true);
        setSubCats([]);
        SubCategoryService.get(catId).then((response) => {
            setSubCats(response.data);
            setLoader(false);
            scrollRef.current.scrollIntoView({ behavior: "smooth" });
        });
    }

    function showProductPopupHandler(productId) {
        ProductService.get(productId).then((response) => {
            const product = response.data;

            return productModal.fire({
                html:
                    <div>
                        <h3 className={modalStyles.header}>{product.AMP_Title}</h3>
                        <h4 className={`${modalStyles.price} ${modalStyles.header}`}>{"CHf " + product.AMP_Price}</h4>
                        <img className={modalStyles.image} src={process.env.REACT_APP_BACKEND_BASE_URL + 'images/products/' + product.AMP_Image} />
                        {
                            product.descriptions.map((desc, _) => (
                                <div key={desc.AMPD_GUID}>
                                    {
                                        desc.AMPD_Title === '1' && <h4 className={modalStyles.header}>{product.AMP_Title}</h4>
                                    }
                                    {
                                        desc.AMPD_Image !== '' && <img src={process.env.REACT_APP_BACKEND_BASE_URL + 'images/descriptions/' + desc.AMPD_Image} className={desc.AMPD_Alignment === 'left' ? modalStyles.descImageLeft : modalStyles.descImageRight} />
                                    }
                                    <p className={modalStyles.description}>{desc.AMPD_Description}</p>
                                </div>
                            ))
                        }
                        <br/>
                        <div>
                            <FavoriteBtn productId={product.AMP_GUID}/>
                            <ShoppingCartQuantity productId={product.AMP_GUID} />                          
                        </div>
                        <br/>
                        {
                            product.AMP_VideoId !== '' &&
                            <YouTube
                                videoId={product.AMP_VideoId}
                                opts={opts}
                            />
                        }
                    </div>,
                showConfirmButton: false,
                showCloseButton: true
            });
        });
    }

    return (
        <div>
            <div className="categories-container">
                <ResponsiveGridLayout
                    className="layout"
                    layouts={ CategoriesLayout }
                    breakpoints={ { lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 } }
                    cols={ { lg: 8, md: 8, sm: 6, xs: 4, xxs: 2 } }
                    isDraggable={ false }
                >
                    {
                        categories.map((cat, index) => (
                            <div key={index}>
                                <CategoryCard
                                    category_id = {cat.AMC_GUID}
                                    image = {cat.AMC_Image}
                                    showSubCatsHandler = {showSubCatsHandler}
                                />
                            </div>
                        ))
                    }
                </ResponsiveGridLayout>
                {
                    loader &&
                    <Triangle
                        height="100"
                        width="100"
                        color='#bcad70'
                        ariaLabel='loading'
                        wrapperClass="loader"
                    />
                }
                <div ref={scrollRef}>
                    {
                        Object.keys(subCats).map((keyName, _) => (
                            <Subcat
                                key={subCats[keyName].AMSC_GUID}
                                title={ subCats[keyName].AMSC_Title }
                                products={ subCats[keyName].products }
                                showProductPopupHandler = {showProductPopupHandler}
                            />
                        ))
                    }
                </div>
            </div>
        </div>
    );
}

export default Homepage;

我在 sweetalert 弹出窗口中使用了组件。我想是 sweetAlert 组件无法访问上下文。有人知道如何将上下文传递给 sweetAlert 组件吗?

更新2

接受的解决方案效果很好,除了1个小问题: shoppingcartquantity 组件在 sweetAlert popup和 sweetaLert > QTY 不会在视觉上更改。

我通过使用 QTY 作为 state 更新了组件。

const ShoppingCartQuantity = ({ qty, productId, updateCart }) => {
    const [quantity, setQuantity] = useState(qty);

    const updateCartHandler = (productId, amount) => {
        updateCart(productId, amount);
        setQuantity(Math.max(quantity + amount, 1));
    }

    return (
        <div>
            <span>
                <span
                    className={`${styles.op} ${styles.decrementBtn}`}
                    onClick={() => updateCartHandler(productId, -1)}
                >
                  -
                </span>
                <span className={styles.qty}>{quantity}</span>
                <span
                    className={`${styles.op} ${styles.incrementBtn}`}
                    onClick={() => updateCartHandler(productId, 1)}
                >
                  +
                </span>
            </span>
        </div>
    )
}

My context is as follows:

import React, {createContext, useEffect, useState} from "react";

export const CartContext = createContext();

const CartContextProvider = (props) => {
    const [cart, setCart] = useState(JSON.parse(localStorage.getItem('cart')) || []);

    useEffect(() => {
        localStorage.setItem('cart', JSON.stringify(cart));
    }, [cart]);

    const updateCart = (productId, op) => {
        let updatedCart = [...cart];

        if (updatedCart.find(item => item.id === productId)) {
            let objIndex = updatedCart.findIndex((item => item.id === productId));

            if (op === '-' && updatedCart[objIndex].qty > 1) {
                updatedCart[objIndex].qty -= 1;
            } else if (op === '+') {
                updatedCart[objIndex].qty += 1;
            }
        } else {
            updatedCart.push({id: productId, qty: 1})
        }

        setCart(updatedCart);
    }

    const removeItem = (id) => {
        setCart(cart.filter(item => item.id !== id));
    };

    return (
        <CartContext.Provider value={{cart, updateCart, removeItem}}>
            {props.children}
        </CartContext.Provider>
    )
};

export default CartContextProvider;

App.js:

import React from "react";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import NavigationBar from "./components/layout/navigationBar/NavigationBar";
import Homepage from "./pages/homepage/Homepage";
import AboutUsPage from "./pages/aboutUs/AboutUsPage";
import ContactPage from "./pages/contact/ContactPage";
import SearchPage from "./pages/search/SearchPage";
import ShoppingCart from "./components/layout/shoppingCart/ShoppingCart";
import CartContextProvider from "./context/CartContext";

function App() {
    return (
        <div>
            <CartContextProvider>
                <Router>
                    <NavigationBar/>
                    <ShoppingCart/>
                    <Routes>
                        <Route exact path="/" element={<Homepage/>}/>
                        <Route path="/a-propos" element={<AboutUsPage/>} />
                        <Route path="/contact" element={<ContactPage/>}/>
                        <Route path="/recherche" element={<SearchPage/>}/>
                    </Routes>
                </Router>
            </CartContextProvider>
        </div>
    );
}

export default App;

In the component ShoppingCart I am using another component ShoppingCartQuantity which in turn makes use of the context. It works as it should.

Here's the ShoppingCartQuantity component:

import React, {useContext} from "react";
import {CartContext} from "../../../context/CartContext";

import styles from './ShoppingCartQuantity.module.css'

const ShoppingCartQuantity = ({productId}) => {
    const {cart, updateCart} = useContext(CartContext);

    let qty = 0;
    if (cart.find((item => item.id === productId))) {
        let objIndex = cart.findIndex((item => item.id === productId));

        qty = cart[objIndex].qty;
    }

    return (
        <div>
            <span>
                <span className={`${styles.op} ${styles.decrementBtn}`} onClick={() => updateCart(productId, '-')}>-</span>
                <span className={styles.qty}>{qty}</span>
                <span className={`${styles.op} ${styles.incrementBtn}`} onClick={() => updateCart(productId, '+')}>+</span>
            </span>
        </div>
    )
}

export default ShoppingCartQuantity;

Now I am trying to use the ShoppingCartQuantity component in the Homepage component which is a route element (refer to App.js) but getting the error Uncaught TypeError: Cannot destructure property 'cart' of '(0 , react__WEBPACK_IMPORTED_MODULE_0__.useContext)(...)' as it is undefined.

So the context is working for components outside the router but not for those inside it. If I have wrapped the router within the provider, shouldn't all the route elements get access to the context or am I missing something?

UPDATE

As user Build Though suggested in the comments, I tried using the ShoppingCartQuantity component in another route element and it works fine; so the problem is not with the router!

Below is the code of how I am using the ShoppingCartQuantity component in the Homepage component:

import React, { useState, useEffect,  useRef } from "react";
import { Responsive, WidthProvider } from "react-grid-layout";
import Subcat from "../../components/subcat/Subcat";
import CategoryService from "../../services/api/Category";
import SubCategoryService from "../../services/api/SubCategory";
import CategoriesLayout from "../../utils/CategoriesLayout";
import CategoryCard from "../../components/category/CategoryCard";
import { Triangle } from  'react-loader-spinner'
import ScrollIntoView from 'react-scroll-into-view'
import ProductService from "../../services/api/Product";
import Swal from 'sweetalert2'
import withReactContent from 'sweetalert2-react-content';
import YouTube from 'react-youtube';
import FavoriteBtn from "../../components/favorite/FavoriteBtn";
import ShoppingCartQuantity from "../../components/layout/shoppingCart/ShoppingCartQuantity";

import "./Homepage.css";
import "../../components/product/ProductModal.css"
import "react-loader-spinner";
import modalStyles from "../../components/product/ProductModal.module.css"

function Homepage() {
    const [categories, setCategories] = useState([]);
    const [subCats, setSubCats] = useState([]);
    const [loader, setLoader] = useState(false);
    const ResponsiveGridLayout = WidthProvider(Responsive);
    const scrollRef = useRef();
    const productModal = withReactContent(Swal);
    const opts = {
        // height: '390',
        // width: '640',
        playerVars: {
            autoplay: 1,
        }
    };

    useEffect(() => {
        CategoryService.get().then((response) => {
            setCategories(response);
        });
    }, []);

    function showSubCatsHandler(catId) {
        setLoader(true);
        setSubCats([]);
        SubCategoryService.get(catId).then((response) => {
            setSubCats(response.data);
            setLoader(false);
            scrollRef.current.scrollIntoView({ behavior: "smooth" });
        });
    }

    function showProductPopupHandler(productId) {
        ProductService.get(productId).then((response) => {
            const product = response.data;

            return productModal.fire({
                html:
                    <div>
                        <h3 className={modalStyles.header}>{product.AMP_Title}</h3>
                        <h4 className={`${modalStyles.price} ${modalStyles.header}`}>{"CHf " + product.AMP_Price}</h4>
                        <img className={modalStyles.image} src={process.env.REACT_APP_BACKEND_BASE_URL + 'images/products/' + product.AMP_Image} />
                        {
                            product.descriptions.map((desc, _) => (
                                <div key={desc.AMPD_GUID}>
                                    {
                                        desc.AMPD_Title === '1' && <h4 className={modalStyles.header}>{product.AMP_Title}</h4>
                                    }
                                    {
                                        desc.AMPD_Image !== '' && <img src={process.env.REACT_APP_BACKEND_BASE_URL + 'images/descriptions/' + desc.AMPD_Image} className={desc.AMPD_Alignment === 'left' ? modalStyles.descImageLeft : modalStyles.descImageRight} />
                                    }
                                    <p className={modalStyles.description}>{desc.AMPD_Description}</p>
                                </div>
                            ))
                        }
                        <br/>
                        <div>
                            <FavoriteBtn productId={product.AMP_GUID}/>
                            <ShoppingCartQuantity productId={product.AMP_GUID} />                          
                        </div>
                        <br/>
                        {
                            product.AMP_VideoId !== '' &&
                            <YouTube
                                videoId={product.AMP_VideoId}
                                opts={opts}
                            />
                        }
                    </div>,
                showConfirmButton: false,
                showCloseButton: true
            });
        });
    }

    return (
        <div>
            <div className="categories-container">
                <ResponsiveGridLayout
                    className="layout"
                    layouts={ CategoriesLayout }
                    breakpoints={ { lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0 } }
                    cols={ { lg: 8, md: 8, sm: 6, xs: 4, xxs: 2 } }
                    isDraggable={ false }
                >
                    {
                        categories.map((cat, index) => (
                            <div key={index}>
                                <CategoryCard
                                    category_id = {cat.AMC_GUID}
                                    image = {cat.AMC_Image}
                                    showSubCatsHandler = {showSubCatsHandler}
                                />
                            </div>
                        ))
                    }
                </ResponsiveGridLayout>
                {
                    loader &&
                    <Triangle
                        height="100"
                        width="100"
                        color='#bcad70'
                        ariaLabel='loading'
                        wrapperClass="loader"
                    />
                }
                <div ref={scrollRef}>
                    {
                        Object.keys(subCats).map((keyName, _) => (
                            <Subcat
                                key={subCats[keyName].AMSC_GUID}
                                title={ subCats[keyName].AMSC_Title }
                                products={ subCats[keyName].products }
                                showProductPopupHandler = {showProductPopupHandler}
                            />
                        ))
                    }
                </div>
            </div>
        </div>
    );
}

export default Homepage;

I am using the component in a SweetAlert popup. I guess it's the SweetAlert component that is not getting access to the context. Does anyone have an idea how to pass the context to the SweetAlert component?

UPDATE 2

The accepted solution works great except for 1 small issue: the ShoppingCartQuantity component was not re-rendering inside the SweetAlert popup and the qty would not change visually.

I updated the component by using the qty as a state.

const ShoppingCartQuantity = ({ qty, productId, updateCart }) => {
    const [quantity, setQuantity] = useState(qty);

    const updateCartHandler = (productId, amount) => {
        updateCart(productId, amount);
        setQuantity(Math.max(quantity + amount, 1));
    }

    return (
        <div>
            <span>
                <span
                    className={`${styles.op} ${styles.decrementBtn}`}
                    onClick={() => updateCartHandler(productId, -1)}
                >
                  -
                </span>
                <span className={styles.qty}>{quantity}</span>
                <span
                    className={`${styles.op} ${styles.incrementBtn}`}
                    onClick={() => updateCartHandler(productId, 1)}
                >
                  +
                </span>
            </span>
        </div>
    )
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

亣腦蒛氧 2025-01-31 15:54:33

问题

很有可能使您的应用程序呈现 ,因此, cartContextProvider 提供商之外。我刚刚搜索了存储库文档,如果有一种指定根元素的方法,但这似乎不可能,因为此甜美的警报代码不是针对反应的。

请参阅此其他类似的问题关于在警报中访问redux上下文。

解决方案

似乎不可能从模态内访问上下文值,因此,恕我直言,解决方法可能是将您的 shoppingcartquantity 组件重构到包装程序容器组件中,以访问上下文和演示成分接收上下文值和任何回调。

我还建议仅将您要增加/减少数量的金额 updatecart ,而不是传递“+”+“ /” - “ - ” - “” 字符串和操作员比较。

示例:

export const withShoppingCartContext = Component => props => {
  const { cart, removeItem, updateCart } = useContext(CartContext);
  return <Component {...props} {...{ cart, removeItem, updateCart }} />;
}

const ShoppingCartQuantity = ({ cart, productId, updateCart }) => {
  const qty = cart.find(item => item.id === productId)?.qty ?? 0;

  return (
    <div>
      <span>
        <span
          className={`${styles.op} ${styles.decrementBtn}`}
          onClick={() => updateCart(productId, -1)}
        >
          -
        </span>
        <span className={styles.qty}>{qty}</span>
        <span
          className={`${styles.op} ${styles.incrementBtn}`}
          onClick={() => updateCart(productId, 1)}
        >
          +
        </span>
      </span>
    </div>
  )
}

export default ShoppingCartQuantity;

在您的应用中的某个地方, shoppingCartQuantity 组件在 cartContextProvider 中使用使用使用ShoppingCartContext hoc和正常使用。

购物车

import ShoppingCartQuantityBase, {
  withShoppingCartContext
} from "../../components/layout/shoppingCart/ShoppingCartQuantity";

const ShoppingCartQuantity = withShoppingCartContext(ShoppingCartQuantityBase);

const ShoppingCart = (props) => {
  ...

  return (
    ...
    <ShoppingCartQuantity productId={....} />
    ...
  );
};

shoppingcartquantity 组件外部上下文的地方

...
import ShoppingCartQuantity from "../../components/layout/shoppingCart/ShoppingCartQuantity";
...

function Homepage() {
  ...
  const { cart, updateCart } = useContext(CartContext);
  const productModal = withReactContent(Swal);
  ...

  function showProductPopupHandler(productId) {
    ProductService.get(productId)
      .then((response) => {
        const product = response.data;

        return productModal.fire({
          html:
            <div>
              ...
              <div>
                <FavoriteBtn productId={product.AMP_GUID}/>
                <ShoppingCartQuantity
                  productId={product.AMP_GUID}
                  {...{ cart, updateCart }}
                />                          
              </div>
              ...
            </div>,
          showConfirmButton: false,
          showCloseButton: true
        });
      });
  }

  return (...);
}

export default Homepage;

,就像在甜蜜模式中一样,访问React代码中的上下文并通过上下文值和回调。其他问题

您的上下文提供商在更新数量时正在突变状态。更新嵌套状态时,您仍应创建正在更新的数组元素的浅副本。

例子:

const CartContextProvider = (props) => {
  ...

  const updateCart = (productId, amount) => {
    // only update if item in cart
    if (cart.some(item => item.id === productId)) {
      // use functional state update to update from previous state
      // cart.map creates shallow copy of previous state
      setCart(cart => cart.map(item => item.id === productId
        ? {
          ...item, // copy item being updated into new object reference
          qty: Math.max(item.qty + amount, 1), // minimum quantity is 1
        }
        : item
      ));
    }
  }

  const removeItem = (id) => {
    setCart(cart => cart.filter(item => item.id !== id));
  };

  return (
    <CartContext.Provider value={{ cart, updateCart, removeItem }}>
      {props.children}
    </CartContext.Provider>
  );
};

Issue

It's very likely that the sweet alert component is rendered outside your app, and thus, outside the CartContextProvider provider. I just searched the repo docs if there is a way to specify a root element, but this doesn't seem possible since this sweet alert code isn't React specific.

See this other similar issue regarding accessing a Redux context in the alert.

Solution

It doesn't seem possible ATM to access the context value from within the modal, so IMHO a workaround could be to refactor your ShoppingCartQuantity component into a wrapper container component to access the context and a presentation component to receive the context values and any callbacks.

I suggest also just passing the amount you want to increment/decrement the quantity by to updateCart instead of passing a "+"/"-" string and operator comparison.

Example:

export const withShoppingCartContext = Component => props => {
  const { cart, removeItem, updateCart } = useContext(CartContext);
  return <Component {...props} {...{ cart, removeItem, updateCart }} />;
}

const ShoppingCartQuantity = ({ cart, productId, updateCart }) => {
  const qty = cart.find(item => item.id === productId)?.qty ?? 0;

  return (
    <div>
      <span>
        <span
          className={`${styles.op} ${styles.decrementBtn}`}
          onClick={() => updateCart(productId, -1)}
        >
          -
        </span>
        <span className={styles.qty}>{qty}</span>
        <span
          className={`${styles.op} ${styles.incrementBtn}`}
          onClick={() => updateCart(productId, 1)}
        >
          +
        </span>
      </span>
    </div>
  )
}

export default ShoppingCartQuantity;

In places in your app where ShoppingCartQuantity component is used within the CartContextProvider decorate it with the withShoppingCartContext HOC and use normally.

ShoppingCart

import ShoppingCartQuantityBase, {
  withShoppingCartContext
} from "../../components/layout/shoppingCart/ShoppingCartQuantity";

const ShoppingCartQuantity = withShoppingCartContext(ShoppingCartQuantityBase);

const ShoppingCart = (props) => {
  ...

  return (
    ...
    <ShoppingCartQuantity productId={....} />
    ...
  );
};

In places where ShoppingCartQuantity component is used outside the context, like in the sweet modal, access the context within the React code and pass in the context values and callbacks.

...
import ShoppingCartQuantity from "../../components/layout/shoppingCart/ShoppingCartQuantity";
...

function Homepage() {
  ...
  const { cart, updateCart } = useContext(CartContext);
  const productModal = withReactContent(Swal);
  ...

  function showProductPopupHandler(productId) {
    ProductService.get(productId)
      .then((response) => {
        const product = response.data;

        return productModal.fire({
          html:
            <div>
              ...
              <div>
                <FavoriteBtn productId={product.AMP_GUID}/>
                <ShoppingCartQuantity
                  productId={product.AMP_GUID}
                  {...{ cart, updateCart }}
                />                          
              </div>
              ...
            </div>,
          showConfirmButton: false,
          showCloseButton: true
        });
      });
  }

  return (...);
}

export default Homepage;

Additional Issues

Your context provider is mutating state when updating quantities. When updating nested state you should still create a shallow copy of the array elements that are being updated.

Example:

const CartContextProvider = (props) => {
  ...

  const updateCart = (productId, amount) => {
    // only update if item in cart
    if (cart.some(item => item.id === productId)) {
      // use functional state update to update from previous state
      // cart.map creates shallow copy of previous state
      setCart(cart => cart.map(item => item.id === productId
        ? {
          ...item, // copy item being updated into new object reference
          qty: Math.max(item.qty + amount, 1), // minimum quantity is 1
        }
        : item
      ));
    }
  }

  const removeItem = (id) => {
    setCart(cart => cart.filter(item => item.id !== id));
  };

  return (
    <CartContext.Provider value={{ cart, updateCart, removeItem }}>
      {props.children}
    </CartContext.Provider>
  );
};
薔薇婲 2025-01-31 15:54:33

您没有显示在哪里使用购物车组件或购物车组件。
无论如何,当您声明路由时,必须通过组件,而不是根元素。因此,这一行:
&lt; oute cresent path =“/” element = {&lt; homepage/&gt;}/&gt;
必须是
&lt; route cresent路径=“/”组件= {homepage}/&gt;

You did't show where you are using the ShoppingCart component or the ShoppingCartQuantity component.
Anyway, when you declare a route, you must pass the component, not the root element. So, this line:
<Route exact path="/" element={<Homepage/>}/>
must be
<Route exact path="/" component={Homepage}/>

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文