Search code examples
javascriptreactjsredux-toolkit

React OnClick Event behavior is unexpected


Product Card

function ProductCard ({image, name, stats, id}){

    let dispatch =  useDispatch()
    let quantity = 1

    return (
        <>
        <div className="product-card">
                <div className="product-image-wrapper">
                    <img src={image} alt="" />
                </div>
                <div className="image-metadata">
                    <div className="data-wrapper">
                        <div className="product-details">
                            <div className="product-category">{stats.category}</div>
                            <div className="product-name">{name}</div>
                        </div>
                        <div className="action-btn-wrapper">
                            <div className="add-to-cart" onClick={()=>{
                                    dispatch(addToCart({name,image,stats,id,quantity}))
                                }
                            }>
                                <img src={addToCartIcon} alt="" />
                            </div>
                        </div>
                    </div>
                </div>
                
            </div>
        </>
    )
}

CART Component

import React, { useEffect } from "react";
import Header from "../components/header";

import Footer from "../components/footer";
import Checkout from "../components/checkout";

import { useSelector } from "react-redux";


function Cart (){
    let cartCount = useSelector(state => state.cart.cart)
    
    useEffect(()=>{
        document.title  = `TRAX- Cart(${cartCount.length} Items)`
    },[cartCount])
    return (
        <>
            <Header/>
            <Checkout/>
            <Footer/>
        </>
    )
}

export default Cart;

Checkout Component

import React from "react";
import cartImage from '../assets/website-icons/9026048_shopping_cart_simple_icon.svg'
import productImage from '../assets/featured/chair-model.jpg'
import arabicImage from '../assets/featured/arabic-model.jpg'

import { useSelector } from "react-redux";
import CartItem from "../widgets/cartItem";


function Checkout(){
    let cartCount = useSelector(state => state.cart.cart)
    return (
        <div className="cart-wrapper">
                <div className="cart-page-icon">
                    <div className="icon-container">
                        <img src= {cartImage} alt="Shopping Cart Icon" />
                        {/* CART */}
                    </div>
                </div>
                <div className="mini-slogan-wrapper">
                    Shop for upto $200 to enjoy<span>FREE SHIPPING</span>.
                </div>

                <div className="cart-table-content">
                    <div className="cart-table">
                        <div className="table-cell header-cell">
                            <div className="table-col-1">Product</div>
                            <div className="table-col-2">Quantity</div>
                            <div className="table-col-3">Price</div>
                            <div className="table-col-4">Subtotal</div>
                        </div>
                        {
                            cartCount.map(({name,image,stats,id,quantity}) => {
                                return <CartItem id={id} productImage={image}  quantity={quantity} productStats={{category : stats.category, color : stats.color}} productName={name}/>
                            })
                        }
                        {/* <div className="table-cell">
                            <div className="table-col-1">
                                <div className="product-entry">
                                    <div className="product-image">
                                        <img src={productImage} alt="" />
                                    </div>
                                    <div className="product-description">
                                        <div className="product-name">Sunny T-Shirt Merch By Zomato</div>
                                        <div className="product-extras">Color: Red, Size : XL, Fabric : Cotton</div>
                                        <div className="product-remove-btn">Remove</div>
                                    </div>
                                </div>
                            </div>
                            <div className="table-col-2">
                                <div className="quantity-adjuster">
                                    <div className="decrementor">
                                        <img src={decrementor} alt="" />
                                    </div>
                                    <div className="quantity-value">1</div>
                                    <div className="incrementor">
                                        <img src={incrementor} alt="" />
                                    </div>
                                </div>
                            </div>
                            <div className="table-col-3">$300</div>
                            <div className="table-col-4">$300</div>
                        </div>
                        <div className="table-cell">
                            <div className="table-col-1">
                                <div className="product-entry">
                                    <div className="product-image">
                                        <img src={arabicImage} alt="" />
                                    </div>
                                    <div className="product-description">
                                        <div className="product-name">Arabic Scarf - Embroidery Print</div>
                                        <div className="product-extras">Color: Mehroon, Size : Standard, Fabric : Linen</div>
                                        <div className="product-remove-btn">Remove</div>
                                    </div>
                                </div>
                            </div>
                            <div className="table-col-2">
                                <div className="quantity-adjuster">
                                    <div className="decrementor">
                                        <img src={decrementor} alt="" />
                                    </div>
                                    <div className="quantity-value">1</div>
                                    <div className="incrementor">
                                        <img src={incrementor} alt="" />
                                    </div>
                                </div>
                            </div>
                            <div className="table-col-3">$120</div>
                            <div className="table-col-4">$120</div>
                        </div> */}
                    </div>
                    <div className="checkout-container">
                        <div className="checkout-heading">
                            Cart Summary
                        </div>
                        <div className="selection-group">
                            <div className="selection-option">
                                <div className="selection-indicator-wrapper">
                                    <div className="selection-ball"></div>
                                </div>
                                <div className="selection-value">Regular Shipping</div>
                                <div className="selection-cost">$12.00</div>
                            </div>
                            <div className="selection-option">
                                <div className="selection-indicator-wrapper">
                                    <div className="selection-balls"></div>
                                </div>
                                <div className="selection-value">Express Shipping</div>
                                <div className="selection-cost">20.00</div>
                            </div>
                        </div>
                        <div className="checkout-secondary-row" id="checkout-subtotal">
                            <div className="heading-secondary-row">Subtotal</div>
                            <div className="cost-secondary-row">$420</div>
                        </div>
                        <div className="checkout-secondary-row" id="checkout-tax">
                            <div className="heading-secondary-row">Tax</div>
                            <div className="cost-secondary-row">$10</div>
                        </div>
                        <div className="checkout-total" id="checkout-total" >
                            <div className="heading-total">Total</div>
                            <div className="cost-total">$430</div>
                        </div>

                        <div className="checkout-btn">Checkout</div>
                    </div>
                </div>
        </div>
    )
}

export default Checkout;

Cart Item Component

import React from "react";
import decrementor from '../assets/website-icons/decrementor.svg'
import incrementor from '../assets/website-icons/incrementor.svg'
import { useDispatch } from "react-redux";
import { removeFromCart,incrementQty,decrementQty } from "../state-manager/Slices/cartSlice";


function CartItem ({productImage,productName, productStats,id,quantity}){
    let dispatch = useDispatch()
    return (
        <div className="table-cell">
            <div className="table-col-1">
                <div className="product-entry">
                    <div className="product-image">
                        <img src={productImage} alt="" />
                    </div>
                    <div className="product-description">
                        <div className="product-name">Sunny T-Shirt Merch By Zomato</div>
                        <div className="product-extras">Color:{productStats.color}, Size : XL, Fabric : Cotton</div>
                        <div className="product-remove-btn" onClick={()=>{
                            dispatch(removeFromCart({productImage,productName,productStats,id}))
                        }}>Remove</div>
                    </div>
                </div>
            </div>
            <div className="table-col-2">
                <div className="quantity-adjuster">
                    <div className="decrementor" onClick={()=>{
                        dispatch(decrementQty({id}))
                    }}>
                        <img src={decrementor} alt="" />
                    </div>
                    <div className="quantity-value">{quantity}</div>
                    <div className="incrementor" onClick={()=>{
                        dispatch(incrementQty({id}))
                    }}>
                        <img src={incrementor} alt="" />
                    </div>
                </div>
            </div>
            <div className="table-col-3">${300}</div>
            <div className="table-col-4">${quantity * 300}</div>
        </div>
    )
}

export default CartItem;

CardSlice

import { createSlice } from "@reduxjs/toolkit"

let CartSlice = createSlice({
    name : 'Cart',
    initialState : {
        cart : [],
        checkoutTotal : 0
    },
    reducers : {
        addToCart : (state,action)=>{
            
            if(state.cart.length < 1){
                state.cart = [...state.cart, action.payload]    
            }else {
                state.cart.map(item => {
                    if(item.id == action.payload.id){
                        item.quantity++;
                    }else {
                        state.cart = [...state.cart, action.payload]

                    }
                })
            }
        },
        removeFromCart : (state,action)=>{
            state.cart.map((item, index, cartItemArray) =>{
                if(item.id == action.payload.id){
                    let rar = cartItemArray.splice(index,1)
                    console.log(rar)
                }       
            })
            
        },
        incrementQty : (state,action)=>{
            state.cart.map(item =>{
                console.log(action.payload)
                if(item.id == action.payload.id){
                    item.quantity = item.quantity + 1
                }
            })
        }, 
        decrementQty : (state,action)=>{
            state.cart.map(item =>{
                
                if(item.id == action.payload.id){
                    if(item.quantity > 0){
                        item.quantity = item.quantity - 1
                    }else {
                        console.log('its already nulled out')
                    }
                }
            })
        }
    }
})

export default CartSlice.reducer
export const {addToCart,removeFromCart, incrementQty,decrementQty}  = CartSlice.actions

I'm using redux for state management, whenever I add to cart, upon the third click the cart count starts doubling up like from 2 to 4 and from 4 to 6 and so on, I have tried a lot and I can't wrap my head around it. Please Let me know what am I doing wrong?


Solution

  • This happens because you loop over all items in the cart, and for each item, you append the entire cart into the cart, here, in the else:

                state.cart.map(item => {
                    if(item.id == action.payload.id){
                        item.quantity++;
                    }else {
                        state.cart = [...state.cart, action.payload]
    
                    }
                })
    

    What you need to do instead is just update the existing item with the new quantity. Something like this:

                const existing = state.cart.find(item => item.id === action.payload.id);
                if (existing) {
                  existing.quantity ++;
                } else {
                  state.cart.push(action.payload);
                }
    

    I cannot test the code, but I hope it will help you identify the problem.

    Please let me know if this helps.