javascriptreactjsroutesreact-hooksreact-bootstrap

Updated total price when buy button is clicked in react.js


I have created a multi-page web application with products avaliable to purchase - as specificed is the task brief. However my Buy button doesn't work as it doesn't store the price of the product to be able to store and add to a total when clicked. also I am trying to create a priceTotal function to take in the prices of the products clicked but it doesn't execute the function I think missing some code? my idea was use props to pass the values through Products.js to calculate the total but I dont think that's the right approach. Help!!! :(

Notes: my products are in the form of an array of objects as per the task brief and with react bootstrap styling :D

App.js

// import react router dom and components //
import { useState } from "react";
import { Routes, Route } from "react-router-dom";
import Home from "./components/Home";
import Products from "./components/Products";
import About from "./components/About";
import LoginPage from "./components/LoginPage";
import TotalPrice from "./components/TotalPrice";
import NavMenu from "./components/NavMenu";
import "./App.css";

// Create the App component //
function App() {
  const [total, setTotal] = useState(0);

  return (
    <div className="App">
      <header className="App-header"></header>
      <NavMenu />
      {/* Create Routes to navigate the multipage web application from current location */}
      <Routes>
        <Route exact path="/" element={<Home />} />
        <Route
          path="/Products"
          element={<Products total={total} changeTotal={setTotal} />}
        />
        <Route path="/About" element={<About />} />
        <Route path="/LoginPage" element={<LoginPage />} />
        <Route
          path="/TotalPrice"
          element={<TotalPrice total={total} changeTotal={setTotal} />}
        />
      </Routes>
    </div>
  );
}

export default App;

Products.js

// Import React, useState and bootstrap components //
import React, { useState } from "react";
import Button from "react-bootstrap/Button";
import Card from "react-bootstrap/Card";
import Dropdown from "react-bootstrap/Dropdown";
import TotalPrice from "./TotalPrice";
import "./Box.css";

// create Products component //
export default function Products(props) {
  // create showTotal and total useState variables //
  const [price, setPrice] = useState(0);

  // Create productCards array of objects to store product information such as name, description, price etc //
  const productCards = [
    {
      image:
        "https://www.magicalmakeup.co.uk/cdn/shop/products/rose-champagne-main_1200x.jpg?v=1666704885",
      product_name: "Eye Twinkle",
      description:
        "Fine glitter topper to make any eye look pop and channel your inner fairy.",
      price: 12,
      title: "Select Colour",
      option1: "Blue Lagoon",
      option2: "Fairy Spell",
      option3: "Enchanted Forest",
    },
    {
      image:
        "https://colourpop.com/cdn/shop/files/Bundle-CupidsCalling.jpg?v=1714417894&width=988",
      product_name: "Flutter Blusher",
      description:
        "Velvety baked blusher for that natural flush and innocent fairy essence.",
      price: 15,
      title: "Select Colour",
      option1: "Pretty Petal",
      option2: "Pinched Peach",
      option3: "Pale Pink",
    },
    {
      image:
        "https://www.amorlashes.co.uk/cdn/shop/products/WINGINGIT_2048x2048.jpg?v=1680019595",
      product_name: "Wink Lashes",
      description:
        "Faux Mink lashes to give that ultimate doll eye look, wispy effect to your lashes.",
      price: 7,
      title: "Select Colour",
      option1: "Cat Eye",
      option2: "Dolly Wing",
      option3: "Flutter Baby",
    },
    {
      image:
        "https://plouise.co.uk/cdn/shop/files/Magic-Dust-Twinkle-Toes.jpg?v=1705928290&width=700",
      product_name: "Sparkle dust",
      description:
        "Loose iridescent highlighter that is both for the face and body to give a magical glow.",
      price: 20,
      title: "Select Colour",
      option1: "Pink Cosmo",
      option2: "Lilac Light",
      option3: "Golden Beam",
    },
  ];

  // create renderProducts function to render information in to assigned card and dropdown components //
  const renderProducts = (card, index) => {
    return (
      <Card style={{ width: "18rem" }} key={index} className="box">
        <Card.Img variant="top" src={card.image} />
        <Card.Body>
          <Card.Title>{card.product_name}</Card.Title>
          <Card.Text>{card.description}</Card.Text>
          <Card.Text>£{card.price}</Card.Text>
          <Dropdown key={index}>
            <Dropdown.Toggle variant="danger" id="dropdown-basic">
              {card.title}
            </Dropdown.Toggle>

            <Dropdown.Menu>
              <Dropdown.Item href="#/action-1">{card.option1}</Dropdown.Item>
              <Dropdown.Item href="#/action-2">{card.option2}</Dropdown.Item>
              <Dropdown.Item href="#/action-3">{card.option3}</Dropdown.Item>
            </Dropdown.Menu>
          </Dropdown>
          {/* onClick event handle to toggle Total price to appear once a buy button has been clicked */}
          <Button onClick={priceTotal} value={card.price} variant="primary">
            Buy
          </Button>
        </Card.Body>
      </Card>
    );
  };

  function priceTotal() {
    props.changeTotal(price + props.total);
  }

  return (
    <div>
      <header className="Pages">
        <h1>Products</h1>
      </header>
      <TotalPrice />
      {/* Use .map method to render each product in productCards array */}
      <div className="grid">{productCards.map(renderProducts)}</div>
    </div>
  );
}

TotalPrice.js

// import React //
import React from "react";

// create TotalPrice component //
export default function TotalPrice(props) {
  return (
    <div>
      <header className="Total">
        <h2>Total Price: {prop.total}</h2>
      </header>
    </div>
  );
}

Solution

  • In the priceTotal function you are passing price + props.total as the argument to the changeTotal function. However it looks to me like price here is referring to the state variable created inside the Products component (value initialised to zero), not the price of the item itself. So when priceTotal is called it is not changing the value of total.

    I think a possible solution could be to use

    <Button onClick={() => priceTotal(card.price)} value={card.price} variant="primary">
    
    function priceTotal(increment) {
      props.changeTotal(increment + props.total);
    }
    

    Hope that helps?