Updating product quantity to cart from cardComponent (Redux Toolkit, React)

I’m using redux-tlk for my ecommerce site. This is my cartSlice.js for products that are added to the cart. So far, it’s working well when I manipulate the quantity with increment/decrement buttons inside this cartProduct component which displays a list of products in the cart.

Then, I created ProductDetails component which is rendered when a product is clicked to view the details. Inside, I also try to implement increment/decrement buttons with add to cart button.

Although my AddToCart btn here works well, I can’t display productQuantity. I’m calling the same function from cartSlice.js but it shows that the value is undefined when I try to output product.cartQuantity.

Can somebody check it for me and help me figure out what I’m missing in my code?

//CartSlice.js
import { createSlice } from "@reduxjs/toolkit";
import { toast } from "react-toastify";

const initialState = {
    cartItems:localStorage.getItem('cartItems') ? JSON.parse(localStorage.getItem('cartItems')) : [],
    totalQuantity:0,
    totalAmount:0,
}


const cartSlice = createSlice({
    name:'cart',
    initialState,
    reducers : 
        {
            addToCart(state,action) {
            const cartItemIndex = state.cartItems.findIndex(
                (item) => item.id === action.payload.id);

            if(cartItemIndex >= 0) {
                state.cartItems[cartItemIndex].cartQuantity += 1;
                toast.info(`${action.payload.name} is added to the cart again!`,
                {position:'top-right' })
            }

            else {
                const tempProduct = {...action.payload, cartQuantity:1};
                state.cartItems.push(tempProduct);
                toast.success(`${action.payload.name} is added to the cart!`,
                {position:'top-right'})
            }

            localStorage.setItem("cartItems",JSON.stringify(state.cartItems))
            
        },

        removeCartItem(state,action) {
          const inCartItems =  state.cartItems.filter(
                cartItem => cartItem.id!==action.payload.id
            );
            state.cartItems=inCartItems;
            localStorage.setItem('cartItems', JSON.stringify(state.cartItems))
            toast.error(`${action.payload.name} is removed from the cart!`,
                {position:'bottom-right'})
        },

        decreaseCartQuantity(state,action ) {
                const itemIndex= state.cartItems.findIndex(
                    cartItem => cartItem.id === action.payload.id
                )
                
                if(state.cartItems[itemIndex].cartQuantity > 1) {
                    state.cartItems[itemIndex].cartQuantity-= 1;
                }

                else if(state.cartItems[itemIndex].cartQuantity === 1) {
                    const inCartItems =  state.cartItems.filter(
                        cartItem => cartItem.id!==action.payload.id
                    );
                    state.cartItems=inCartItems;
                    toast.error(`${action.payload.name} is removed from the cart!`,
                        {position:'bottom-right'})
                }
                localStorage.setItem('cartItems', JSON.stringify(state.cartItems))
                
        },

        increaseCartQuantity(state,action) {
            const itemIndex= state.cartItems.findIndex(
                cartItem => cartItem.id === action.payload.id
            )
                state.cartItems[itemIndex].cartQuantity+= 1;
                localStorage.setItem('cartItems', JSON.stringify(state.cartItems))            
        },

        clearAllCart(state) {
            state.cartItems = [];
            toast.error(`The cart is cleared!`,
                        {position:'bottom-right'});
            localStorage.setItem('cartItems', JSON.stringify(state.cartItems));  

        },

        getTotal(state) {
            let {total,quantity} = state.cartItems.reduce((cartTotal, cartItem)=>{
                const {price,cartQuantity} = cartItem;
                const itemTotal = price * cartQuantity;

                cartTotal.total += itemTotal;
                cartTotal.quantity += cartQuantity

                return cartTotal;
            }, {
                total:0,
                quantity:0,
            });

            state.totalQuantity = quantity;
            state.totalAmount = total;
        }

    }
})

export const {addToCart, removeCartItem, decreaseCartQuantity,
    increaseCartQuantity,clearAllCart, getTotal} = cartSlice.actions;
export default cartSlice.reducer;
//CartProduct.js

import styled from '@emotion/styled'
import { Add, ArrowBackIos, Remove } from '@mui/icons-material'
import { Box,Button,IconButton,Paper,Table,TableBody,TableCell,tableCellClasses,TableContainer,TableHead,TableRow,TextField,Typography, useMediaQuery } from '@mui/material'
import React from 'react'
import {useDispatch, useSelector } from 'react-redux'
import { Link} from 'react-router-dom'
import { clearAllCart, decreaseCartQuantity, getTotal, increaseCartQuantity } from '../rtk/app/features/cartSlice'
import { theme } from '../style/theme'


export const CartProduct = () => {

  const cart = useSelector((state)=>state.cart);
  const cartItems = useSelector((state)=>state.cart.cartItems);
  const totalQuantity = useSelector(state=> state.cart.totalQuantity);
  const totalAmount = useSelector(state=>state.cart.totalAmount);

  const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
  const isTablet = useMediaQuery(theme.breakpoints.down('md'));
  const isLaptop = useMediaQuery(theme.breakpoints.down('lg'));

  const SHIPPING_FEES = 7.99;
  const dispatch = useDispatch();
 
  function handleBack() {
    window.history.back();
  }

  function handleRemoveItem(cartItem) {
    dispatch(decreaseCartQuantity(cartItem));
  }

  function handleAddItem(cartItem) {
    dispatch(increaseCartQuantity(cartItem));
  }

  function clearCart() {
    dispatch(clearAllCart());
  }

  React.useEffect(()=>{
    dispatch(getTotal());
  },[cart,dispatch])
  

console.log(cartItems);

  return (
           //styling components
     
       { cartItems.length>=1 && 
        <TableContainer component={Paper}>
         // some content

                    <IconButton size='small' onClick={()=>{handleAddItem(cartItem)}}>
                      <Add color='primary'/>
                    </IconButton>
                      <input type='text' className='quantity-value' 
                      pattern='[0-9]{2}'
                      value={cartItem.cartQuantity}/>
                    <IconButton size='small' onClick={()=>{handleRemoveItem(cartItem)}}>
                      <Remove color='primary'/>
                    </IconButton>
                  </StyledTableCell>
                  <StyledTableCell align='center' width='30%'>
                    $ {cartItem.price}
                  </StyledTableCell>
                  <StyledTableCell align='center'>
                  $ {parseFloat(cartItem.price* cartItem.cartQuantity).toFixed(2)}
                  </StyledTableCell>
                </StyledTableRow>
              ))}

            <TableRow>
              <TableCell rowSpan={4} />
              <TableCell colSpan={2}>Subtotal</TableCell>
              <TableCell align="center" width='100%' sx={{
                fontWeight:'bold',
              }}>$ {parseFloat(totalAmount).toFixed(2)}</TableCell>
            </TableRow>
            <TableRow>
              <TableCell colSpan={2}>Total Quantity</TableCell>
              <TableCell align="center" sx={{
                fontWeight:'bold',
              }}>{totalQuantity}</TableCell>
            </TableRow>
            <TableRow>
              <TableCell colSpan={2}>Shipping Fees</TableCell>
              <TableCell align="center" sx={{
                fontWeight:'bold',
              }}>$ {SHIPPING_FEES}</TableCell>
            </TableRow>
            <TableRow >
              <TableCell colSpan={2}>Total</TableCell>
              <TableCell align="center" sx={{
                fontWeight:'bold',
              }}>$ {parseFloat(SHIPPING_FEES + totalAmount).toFixed(2) }</TableCell>
            </TableRow>
            </TableBody>
          </Table>    

           //This cardProduct.jsx works fine calling the same function from cartSlice.js
//ProductDetails.jsx
import { Add, ArrowBackIos, Circle, Remove } from '@mui/icons-material';
import { Box, Button, Chip, IconButton, Rating, Tooltip, Typography, useMediaQuery } from '@mui/material'
import React from 'react'
import { useDispatch, useSelector } from 'react-redux';
import { useLocation } from 'react-router-dom';
import { addToCart, decreaseCartQuantity, increaseCartQuantity } from '../rtk/app/features/cartSlice';
import { theme } from '../style/theme'

export const ProductDetails = () => {

    const cart = useSelector((state)=>state.cart);
    const cartItems = useSelector((state)=>state.cart.cartItems);
    const [product, setProduct] = React.useState(null);
    const location = useLocation();
    const dispatch = useDispatch();
    const isMobile = useMediaQuery(theme.breakpoints.down('sm'));

    React.useEffect(() => {
    const { state } = location;
    setProduct(state ? state.product : null);
  }, [location,setProduct]);

  
       function handleRemoveItem(product) {
         dispatch(decreaseCartQuantity(product));

       }
    
       function handleAddItem(product) {
        
        dispatch(increaseCartQuantity(product));

       }

       const handleAddToCart = (product) => {
        dispatch(addToCart(product));
     }
     //console.log(product.cartQuantity);
//If I didn't comment it out, the program crashed, claiming product is null. After I rerun the program, it outputs undefined. 

  return (
    <Box pt={10} px={2} mt={5}>
        <IconButton onClick={()=>window.history.back()} size='medium'>
             <ArrowBackIos color='tertiary'/>
        </IconButton>
       {product!=null &&
        <Box sx= {{width:'80%', margin:'1em auto 0 auto', boxShadow:3, padding:'1em 0 0.5em 0'}}>
            <Typography variant='h6' color='secondary' textAlign='center'>
                {product.name}
            </Typography>
            
            <Box display='flex'
            justifyContent='center' 
            flexDirection={isMobile? 'column' : 'row'}>
                <Box py={1} mx={2} width='40%' display='flex'
                justifyContent='center' alignItems='center'
                flexDirection='column'>
                    <Box component='img'
                    sx={{objectFit:'contain'}}
                    src={product.image_link}
                    alt={product.name}
                    width='50%'/>
                    
                    <Typography variant='caption' component='h6'
                    color={theme.palette.primary.light}>
                        Brand: {product.brand}
                    </Typography>
                </Box>
                <Box py={2} mx={2} width='60%'>
                    <Typography variant='caption' component='h6'
                    color={theme.palette.primary.light}
                    gutterBottom pb={2}
                    sx={{width:isMobile? '90%' : '80%'}}>
                        Description:<br/>
                        {product.description}
                    </Typography>

                    {product.category &&
                    <Typography variant='caption' component='h6'
                    color ={theme.palette.primary.light} gutterBottom>
                        Category : {product.category}</Typography>}

                    <Typography variant='caption' component='h6'
                    color ={theme.palette.primary.light} gutterBottom>
                        Rating : 
                        <Rating value={product.rating} name='product_rating' readOnly
                        size='small' sx={{verticalAlign:'middle'}}/>
                    </Typography>
                    
                    
                    {product.product_colors.length >=1 &&
                    <Typography variant='caption' component='h6'
                    color ={theme.palette.primary.light}>Available Colours :&nbsp;
                        {product.product_colors.map((color)=> (
                            <Tooltip title={color.colour_name}>
                                <Circle sx={{color:color.hex_value, verticalAlign:'middle'}}/>
                            </Tooltip>
                        ))}
                        
                    </Typography>}
                    {product.tag_list.length >=1 &&
                    <Typography variant='caption' component='h6'
                    color={theme.palette.primary.light}>Product Tags : 
                        {product.tag_list.map((tag)=>(
                            <Chip label={tag} variant='outlined'/>
                        ))}
                    </Typography>}
                    
                    <IconButton size='small' onClick={()=>{handleAddItem(product)}}>
                        <Add color='primary'/>
                    </IconButton>
                    <input type='number' className='quantity-value' 
                        pattern='[0-9]{2}'
                        value={product.cartQuantity} readOnly/>
                    <IconButton size='small' onClick={()=>{handleRemoveItem(product)}}>
                        <Remove color='primary'/>
                    </IconButton>

                    <Button sx={{background:theme.palette.secondary.main,
                                transition:'background 1s ease-in-out',
                                padding:'0.5em 2em',
                                margin:'1em',
                                color:theme.palette.textColor.main,
                                
                                '&:hover': {
                                    background:theme.palette.primary.main,
                                }}}
                                onClick={()=>{handleAddToCart(product)}}>
                            Add to Cart
                    </Button>

                </Box>
            </Box>
        </Box>}
    </Box>
  )
}

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.