1

I'm stuck in project because my functional component do not re-render when I update this state.

const OneProduct = (props) => {
return (
<div className='productincart'>
    <div className='name'>{props.name}</div> 
    <div className='price'>Price: {props.price}</div>
    <div className='quantity'>{props.quantity}</div>
</div>)
}

const InsideCart = (props) => {
    const cartTemporary = props.sendCart
    const [cart, setCart] = useState([])

    useEffect(() => {
        setCart(cartTemporary)
        }, [cartTemporary])

    const showCart = () => {
        console.log(cart)
    }

    return (

        <div className='insidecart'>
            {cart.map(product => <OneProduct name={product.name} price= 
            {product.price} quantity={product.quantity} key={uniqid()} />)}
            <button onClick={showCart}>SHOW CART</button>
        </div>
    )
    }

In code above, I putting products from shop to the cart, next I want to display it on the screen. When I'm putting new item to the cart (new object in cart array), everything works fine. When I'm only update quantity of object already exist in cart array, nothing happens. After quantity updating, cart is updated with new value, but it doesn't causing re-render of component.

That is problem to solve, how to make component re-render after quantity value update.

Below format of objects which I add to cart, also added mechanism of adding and increasing quantity.

const Products = (props) => {
    const initialProducts = [
        { id: 0, name: "Fish - Betta splendens - Plakat Koi - Siamese fighting fish", image: image0, price: 23.17, quantity: 1 },
        { id: 1, name: "Fish - Betta splendens - Plakat - Siamese fighting fish", image: image1, price: 10.12, quantity: 1 },
        { id: 2, name: "Fish - Phenacogrammus interruptus - Congo tetra", image: image2, price: 5.19, quantity: 1 },
        { id: 3, name: "Fish - Thayeria boehlkei - Pinguin tetra", image: image3, price: 1.31, quantity: 1 },
        { id: 4, name: "Fish - Pterophyllum scalare - Angelfish", image: image4, price: 5.77, quantity: 1 },
        { id: 5, name: "Fish - Boeseman's rainbowfish Melanotaenia - Boesemani", image: image5, price: 4.90, quantity: 1 },
        { id: 6, name: "Fish - Ram cichlid - Mikrogeophagus ramirezi", image: image6, price: 4.61, quantity: 1 },
        { id: 7, name: "Fish - Corydoras aeneus sp.black venezuela", image: image7, price: 2.87, quantity: 1 },

    ]
    const [products, setProducts] = useState(initialProducts)
    const [toCart, setToCart] = useState([])

    const getProductId = (e) => {
        const idString = e.target.dataset.id
        const id = Number(idString)
        return id
    }

    const getProductById = (e) => {
        const id = getProductId(e)
        const itemFound = products.find(product => product.id === id)
        const foundedOrNot = toCart.find(product => product === itemFound)
        if (foundedOrNot === undefined) {
            setToCart([...toCart, itemFound])
        }
        else {
            const index = toCart.findIndex(product => product === itemFound)
            toCart[index].quantity = toCart[index].quantity + 1
        }

    }

    return (
        <div className="products">
            <Nav sendToCart={toCart} />
            <InsideCart sendCart={toCart} />
            <div className='container'>{products.map((product) => <Product onClick={getProductById} title={product.name} img={product.image} price={product.price} id={product.id} key={product.id} />)}</div>
        </div>
    );
};

Also add simple component which determines rendering what is inside the cart.

const OneProduct = (props) => {
    return (<div className='productincart'><div className='name'>{props.name}</div> <div className='price'>Price: {props.price}</div><div className='quantity'>{props.quantity}</div></div>)
}
Arturito
  • 11
  • 3
  • Can you show the code where you update the cart? My guess is that you're updating by reference, instead of calling `setCart` with a new value. – Halcyon Sep 05 '22 at 12:29
  • I don't see where you update the cart. – Anas Latique Sep 05 '22 at 12:29
  • I just flicked through your code and failed to spot any code that actually performs "add to basket" operation. I might suspect, though, you're using `Array.prototype.push()` or `Array.prototype.splice()` method for that and since they mutate arrays in place, React fails to figure out that state has been modified. – Yevhen Horbunkov Sep 05 '22 at 12:29
  • So, most likely, you would want to make state updates immutable by shallow copying initial array, modifying that and then setting your state to updated array. – Yevhen Horbunkov Sep 05 '22 at 12:31
  • And, just a side note, generating `uniqid()` for your `key` is definitely not a good idea from a performance standpoint. I would recommend sticking to product `id` as a key. – Yevhen Horbunkov Sep 05 '22 at 12:38
  • Already added code of component which is responsible for adding product to cart. Thanks for note about the `uniqid()`. Just starting with React :) – Arturito Sep 05 '22 at 12:48
  • Both `product` and `itemFound` seem to be objects. And you can't compare objects with strict equivalence operator (`===`), hence it looks like you never actually update your state. – Yevhen Horbunkov Sep 05 '22 at 15:20
  • If you need to find out whether the item exists within an array, I would suggest [`Array.prototype.some()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some). It will return boolean, so you won't get distracted by types mismatch. – Yevhen Horbunkov Sep 05 '22 at 15:22

1 Answers1

0

I already solved the problem. Issue was not using setToCart in product component. Thank you guys for all advices!

Updated code below:

  const getProductById = (e) => {
        const id = getProductId(e)
        const itemFound = products.find(product => product.id === id)
        const foundedOrNot = toCart.some(product => product === itemFound)
        if (foundedOrNot === false) {
            setToCart([...toCart, itemFound])
        }
        else {
            const index = toCart.findIndex(product => product === itemFound)
            const newCart = toCart.map(cart => {
                if (cart.id === index) {
                    cart.quantity = cart.quantity + 1
                    return cart
                }
                return cart
            })
            setToCart(newCart)
        }

    }
Arturito
  • 11
  • 3
  • You're still doing the same mistake (`product === itemFound` will always returrn `false`), so you will never enter your `else` branch. You're supposed to compare product objects by `id` (`product.id === itemFound.id`) within `some()`. – Yevhen Horbunkov Sep 05 '22 at 19:06
  • Thank you for advice. I will change it in code to write it better. Otherwise, I can't agree `product === itemFound` will always return `false`, because in this case it really return `true`. Of course I realize that, it is bad practice and should be not used. – Arturito Sep 07 '22 at 06:56
  • It's not a matter of good or bad practice, it's just the way [strict equality operator works](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Strict_equality#comparing_objects). In your particular case it may currently return `true` due to the fact that the reference to original objects is preserved but it may lead to some nasty bags in the future. – Yevhen Horbunkov Sep 07 '22 at 11:51