skip to Main Content

I have an object which has a key value of quantity. I want to be able to update the quantity value using an incrementor and decrementor. I am able to do this but unable to make my site live as I need to use the currentState of useState. Currently I’ve tried many ways to incorporate currentState but it never works!

Here is what I have so far:

import React from 'react'
import { Link, useParams } from 'react-router-dom'
import { useState, useContext } from 'react'
import GetSingleProduct from '../Hooks/getSingleProduct'
import { AppContext } from '../Context/Context'
import '../Assets/Styles/Product.css'

function Product() {
  // able to use useParams to direct to the page with the id of the product that has been clicked
  const { id } = useParams()
  const { singleProduct, isLoading, error } = GetSingleProduct(`https://makeup-api.herokuapp.com/api/v1/products/${id}.json`)
  const [clickedColour, setClickedColour] = useState([])

 
  // useContext for the add to cart 
  const Cartstate = useContext(AppContext)
  const dispatch = Cartstate.dispatch;

  const handleColour = (index) => {
    setClickedColour(prevstate =>
    ({
      ...prevstate, [index] // copies prev state
        : !prevstate[index]
    }))
    console.log(setClickedColour)
  }

  const [newQuantity, setNewQuantity] = useState({...singleProduct})
  const handleIncrement = () => {
    const updatedNum = { ...singleProduct, quantity: singleProduct.quantity ++ }
    console.log(updatedNum) 
    console.log(newQuantity)
    setNewQuantity(updatedNum)
  }
  const handleDecrement = () => {
    setNewQuantity({ ...singleProduct, quantity:newQuantity.quantity-- })
  }



  return (
    <>
      <div className='routeTaken'>
        <ol>
          <li><Link to='/'>Home</Link></li>
          <li><Link to='/shop'>Shop</Link></li>
          <li>Current page</li>
        </ol>
      </div>
      <div className='productInfo'>
        {error && <div>{error}</div>}
        {isLoading ?
          (<div>Loading...</div>) :
          <div id={singleProduct.id}>
            <img src={singleProduct.api_featured_image} alt={singleProduct.product_type}></img>
            <p>{singleProduct?.product_type ? singleProduct.product_type.charAt(0).toUpperCase() + singleProduct.product_type.slice(1).toLowerCase().split('_').join(' ') : singleProduct.product_type}</p>

            <h1>{singleProduct?.brand ? singleProduct.brand.charAt(0).toUpperCase() + singleProduct.brand.slice(1).toLowerCase() : singleProduct.brand} {singleProduct?.name ? singleProduct.name.charAt(0).toUpperCase() + singleProduct.name.slice(1).toLowerCase() : singleProduct.name}</h1>
            <h2> £{singleProduct.price==='0.0'||singleProduct.price === null ? '8.50': Number(singleProduct.price).toFixed(2)}</h2>
            <h3>{singleProduct.description}</h3>
            <div className="colourList grid grid-cols-4">
              {singleProduct.product_colors?.map((colour, index) => {
                return (
                  <button onClick={() => handleColour(index)} className='colourItem' key={index} style={{ backgroundColor: colour.hex_value }}>
                    {colour.hex_value}
                    {clickedColour[index] ?
                      (<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-6 h-6">
                        <path strokeLinecap="round" strokeLinejoin="round" d="M4.5 12.75l6 6 9-13.5" />
                      </svg>
                      ) : (null)}
                  </button>
                )
              })}
            </div>
            <div className='counter'>
                <button onClick={handleIncrement} disabled={singleProduct.quantity >= 10}>
                  +
                </button>
                <p>{singleProduct.quantity}</p>
                <button onClick={handleDecrement} disabled={singleProduct.quantity <= 0}>
                  -
                </button>

              </div>
              <button onClick={() => dispatch({ type: 'ADD', payload: singleProduct })}>Add to basket</button>
            </div>
        }
      </div >
    </>
  )
}

export default Product

When I console.log (updatedNum) after each increment button, it shows that it increments but when I do it for newQuantity it does not!

I have already tried:
const updatedNum = { …newQuantity, quantity: newQuantity.quantity+1 }
but it always returns NaN

I’ve also put {newQuantity.quantity} and {singleProduct.quantity} in my JSX to see. It seems newQuantity has a delay and is one count behind the singleProduct

Would really appreciate some help working around this!

2

Answers


  1. You can have several problems with your code:

    1. You log newQuantity just before your actual increment
    2. If you’ll write console.log(newQuantity) just after increment it will not fix the problem cause react state change work asynchronously. Anyway, this should show changed value after multiple clicks.
    3. When you use counter++ notation it means that variable will be incremented but it returns previous value.
    4. You should use state (newQuantity) inside html markup in component instead of singleProduct.

    To make it work you should change this:

      const handleIncrement = () => {
        const updatedNum = { ...singleProduct, quantity: singleProduct.quantity ++ }
        console.log(updatedNum) 
        console.log(newQuantity)
        setNewQuantity(updatedNum)
      }
    

    to this

      const handleIncrement = () => {
        const updatedNum = { ...newQuantity, quantity: (newQuantity.quantity || 0) + 1 }
        console.log(updatedNum)
        setNewQuantity(updatedNum)
      }
    

    It will not print changed value but interface should work as needed.

    To avoid writing (newQuantity.quantity || 0) + 1 and write just newQuantity.quantity + 1 you can change your state initialization to this:

    const [newQuantity, setNewQuantity] = useState({quantity: 0, ...singleProduct})
    
    Login or Signup to reply.
  2. Havn’t tested this, but the problem could be how you’re updating the quantity in the handleIncrement and handleDecrement functions. Using ++ and — directly on singleProduct.quantity doesn’t work well in React. Instead, do it like so:

    const handleIncrement = () => {
      setNewQuantity(prevQuantity => ({
        ...singleProduct,
        quantity: prevQuantity.quantity + 1
      }));
    };
    
    const handleDecrement = () => {
      setNewQuantity(prevQuantity => ({
        ...singleProduct,
        quantity: prevQuantity.quantity - 1
      }));
    };
    

    Here we use setNewQuantity with a callback function that takes the previous quantity (prevQuantity) and then we create a new object by spreading singleProduct and setting quantity to prevQuantity.quantity +/- 1.

    Hope it helps!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search