reactjs – React – pass context to SweetAlert popup

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?

EDIT

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?

Leave a Comment