0

I'm currently struggling with React Context. I'd like to pass functions allowing the show / hide cart logic in the context, instead of using props between components. I dont understand why when clicking on the button in HeaderCartButton component, it doesn't trigger the **onClick={ctx.onShowCart}** that is in my context, even though when I console log the cartCtx.state it is properly updated, which should then add the component in the App.js
//App.js

import { useContext } from "react";

import Header from "./components/Layout/Header";
import Meals from "./components/Meals/Meals";
import Cart from "./components/Cart/Cart";
import CartProvider from "./store/CartProvider";
import CartContext from "./store/cart-context";

function App() {
  const ctx = useContext(CartContext);

  return (
    <CartProvider>
      {ctx.state && <Cart />}
      <Header />
      <main>
        <Meals />
      </main>
    </CartProvider>
  );
}

export default App;
//cart-context.js

import React from "react";

const CartContext = React.createContext({
  state: false,
  onShowCart: () => {},
  onHideCart: () => {},
  items: [],
  totalAmount: 0,
  addItem: (item) => {},
  removeItem: (id) => {},
});

export default CartContext;

//CartProvider.js 

import CartContext from "./cart-context";
import { useState } from "react";

const CartProvider = (props) => {
  const [cartIsShown, setCartIsShown] = useState(false);

  const showCartHandler = () => {
    setCartIsShown(true);
  };

  const hideCartHandler = () => {
    setCartIsShown(false);
  };

  const handleAddItem = (item) => {};

  const handleRemoveItem = (id) => {};

  const cartCtx = {
    state: cartIsShown,
    onShowCart: showCartHandler,
    onHideCart: hideCartHandler,
    items: [],
    totalAmount: 0,
    addItem: handleAddItem,
    removeItem: handleRemoveItem,
  };

  return (
    <CartContext.Provider value={cartCtx}>
      {props.children}
    </CartContext.Provider>
  );
};

export default CartProvider;
//Header.js

import { Fragment } from "react";

import HeaderCartButton from "./HeaderCartButton";
import mealsImage from "../../assets/meals.jpg";
import classes from "./Header.module.css";

const Header = (props) => {
  return (
    <Fragment>
      <header className={classes.header}>
        <h1>ReactMeals</h1>
        <HeaderCartButton />
      </header>
      <div className={classes["main-image"]}>
        <img src={mealsImage} alt="A table full of delicious food!" />
      </div>
    </Fragment>
  );
};

export default Header;
//HeaderCartButton.js

import CartIcon from "../Cart/CartIcon";
import { useContext } from "react";
import classes from "./HeaderCartButton.module.css";
import CartContext from "../../store/cart-context";

const HeaderCartButton = (props) => {
  const ctx = useContext(CartContext);
  const numberOfCartItems = ctx.items.reduce((accumulator, item) => {
    return accumulator + item.amount;
  }, 0);

  return (
    <button className={classes.button} onClick={ctx.onShowCart}>
      <span className={classes.icon}>
        <CartIcon />
      </span>
      <span>Your Cart</span>
      <span className={classes.badge}>{numberOfCartItems}</span>
    </button>
  );
};

export default HeaderCartButton;

Thanks for your help

1 Answers1

3

If you look at your App component, you are using CartContext outside the provider.

function App() {
  const ctx = useContext(CartContext);

  return (
    <CartProvider>
      {ctx.state && <Cart />}
      <Header />
      <main>
        <Meals />
      </main>
    </CartProvider>
  );
}

You should modify it so that it is similar to the following, where you are using the context inside the provider.

const Main = () => {
  return <CartProvider><App /></CartProvider>
}

 function App() {
      const ctx = useContext(CartContext);
    
      return (
          <>
          {ctx.state && <Cart />}
          <Header />
          <main>
            <Meals />
          </main>
         </>
      );
    }
Someone Special
  • 12,479
  • 7
  • 45
  • 76