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
You can have several problems with your code:
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.counter++
notation it means that variable will be incremented but it returns previous value.To make it work you should change this:
to this
It will not print changed value but interface should work as needed.
To avoid writing
(newQuantity.quantity || 0) + 1
and write justnewQuantity.quantity + 1
you can change your state initialization to this: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:
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!