Search code examples
reactjsreact-nativereduxactionreducers

cant add existing product in cart in reducer in redux with react native


am working on a react native application that has a maternal shop,am using redux to work on the global state,and in my reducers i have to handle adding to cart,if product has not been existing in the cart or if product exists in the cart, my aim is just to increase its quantity without increasing the total product count here are my actions and reducers

export const ADD_TO_CART = 'ADD_TO_CART';
export const ADD_QUANTITY = 'ADD_QUANTITY';
export const SUB_QUANTITY = 'SUB_QUANTITY';

export const DELETE_ITEM = 'DELETE_ITEM';


export const addToCart = (payload) => {
    type: ADD_TO_CART,
    payload
}

export const addQuantity = (payload) => {
    type: ADD_QUANTITY,
    payload
}

export const subQuantity = (payload) => {
    type: SUB_QUANTITY,
    payload
}

export const deleteItem = (payload) => {
    type: DELETE_ITEM,
    payload
}

my reducers for the cart

import { ADD_TO_CART,DELETE_ITEM,SUB_QUANTITY,ADD_QUANTITY} from '../actions/cartItems';

const initialState = {
    itemsCount : 0,
    cartItems:[],
    cartTotalAmount:0,

}

const cartReducer = (state=initialState, action)=>{
    switch(action.type){
        case ADD_TO_CART:
            let cart = {
                id:action.payload.itemId,
                quantity:action.payload.quantity,
                name:action.payload.itemTitle,
                image:action.payload.itemImg,
                price:action.payload.itemPrice,
                cartAmount:action.payload.quantity * action.payload.itemPrice
            }
            
            if(state.itemsCount === 0){
                state.cartItems.push(cart);//just push the cart
                return {
                    ...state,
                    itemsCount:state.itemsCount+1,
                    cartTotalAmount:state.cartItems.map(item => {
                        state.cartTotalAmount+item.cartAmount
                    })
                }
            }else{
                let exists =false;
                let i =0;
                while(i<state.cartItems.length){
                    if(state.cartItems[i].id === action.payload.itemId){
                        state.cartItems[i].quantity++;
                        exists = true;
                    }
                    return{
                        ...state,
                        itemsCount:state.itemsCount
                    }
                    i++;
                }

                state.cartItems.map((key,item) => {
                    if(item.id === action.payload.itemId){
                        // {...item,quantity:item.quantity+1}
                        state.cartItems[key].quantity++;
                        exists = true
                    }
                    return {
                        ...state,
                        itemsCount:state.itemsCount,
                    }
                })
                if(!exists){
                    let _cart = {
                        id:action.payload.itemId,
                        quantity:action.payload.quantity,
                        name:action.payload.itemTitle,
                        image:action.payload.itemImg,
                        price:action.payload.itemPrice,
                        cartAmount:action.payload.quantity * action.payload.itemPrice
                    }
                    state.cartItems.push(_cart)
                    return {
                        ...state,
                        itemsCount:state.itemsCount+1,
                        cartTotalAmount:state.cartItems.map(item => {
                            state.cartTotalAmount+item.cartAmount
                        })
                    }
                }


            }
        case ADD_QUANTITY:
            return {
                ...state,
                cartItems:state.cartItems.map(
                    item => item.id === action.payload.itemId
                    ? {...item, quantity: item.quantity+1 }
                    : item 
                ),
            }
        case DELETE_ITEM:
            let newCartItems = state.cartItems.filter(
                (item) => {return item.id != action.payload.itemId}
            )
            let count = state.itemsCount-1;
            return {
                ...state,
                itemsCount:count,
                cartItems:newCartItems,
            }
        case SUB_QUANTITY:
            return {
                ...state,
                cartItems:state.cartItems.map(
                    item => item.id === action.payload.itemId 
                    ? {...item, quantity: item.quantity-1 } 
                    : item  
                ),
            }
        
        // case ADD_TO_WISH_LIST:
        //     for(let i=0; i < state.wishListItems.length; i++){
        //         if(state.wishListItems[i].id === action.item.id){
        //             return {
        //                 ...state,
        //                 wishListItems: state.wishListItems.map(item => item.id === action.item.id ?
        //                     { ...item, quantity: item.quantity+1 } :item
        //                 ) ,
        //             }
        //         }
        //         else{
        //             let updatedWishListItems = [...state.wishListItems, action.item];   
        //             let count = state.wishCount + 1;
        //         }
        //     }
        //     return{
        //         ...state,
        //         wishCount : count,
        //         wishListItems :updatedWishListItems
        //     }
        
        // case DELETE_FROM_WISH_LIST:
        //     let newWishListItems = state.wishListItems.filter(
        //         (item)=>{
        //          return item.id!=action.item.id
        //         }
        //      );
            
        //      return {
        //         ...state,
        //         wishListItems : newWishListItems , 
        //     }  
        default:
            return state
    }
        
}  

export default cartReducer;

the first case in the reducer for adding to cart when itemsCount === 0 works however when the cart has more than one item the reducer has failed to execute properly and am stuck help needed


Solution

  • Issues

    1. Don't store the itemsCount and cartTotalAmount values in state, these are easily derived from state data. Storing duplicate or derived data is anti-pattern.

      const initialState = {
        itemsCount : 0, // <-- easily computed from cart items
        cartItems:[],
        cartTotalAmount:0, // <-- easily computed from cart items
      }
      
    2. Don't mutate your state by pushing directly into your cartItems array.

      state.cartItems.push(cart); // <-- mutates state reference
      
    3. You need to search the cartItems first to see if you've already added a cart item.

    Solution

    case ADD_TO_CART:
      const {
        itemId,
        itemImg,
        itemPrice,
        itemTitle,
        quantity,
      } = action.payload;
    
      // search if item is already in cart by item id
      const inCart = state.cartItems.some(item => item.id === itemId);
    
      if (inCart) {
        // already in cart, shallow copy cart items
        return {
          ...state,
          cartItems: state.cartItems.map(item => item.id === itemId ? {
            // found item, shallow copy item and update quantity property
            ...item,
            quantity: item.quantity + 1,
          } : item),
        }
      } else {
        return {
          ...state,
          cartItems: [
            // shallow copy cart items
            ...state.cartItems,
            // add new cart item
            {
              id: itemId,
              quantity: quantity,
              name: itemTitle,
              image: itemImg,
              price: itemPrice,
            }
          ],
        }
      }
    
      ...
    

    You can apply similar updating patterns for other action types that update the cart items.

    To compute the itemsCount in your UI

    const itemsCount = cartItems.reduce((count, { quantity }) => count + quantity, 0)
    

    To compute the cartTotalAmount

    const cartTotalAmount = cartItems.reduce(
      (totalAmount, { price, quantity }) => totalAmount + quantity * price,
      0,
    );
    

    These can be combined into a single calculation in a single pass

    const { cartTotalAmount, itemsCount } = cartItems.reduce(
      ({ cartTotalAmount, itemsCount }, { price, quantity }) => ({
        cartTotalAmount: cartTotalAmount + quantity * price,
        itemsCount: itemsCount + quantity,
      }),
      {
        cartTotalAmount: 0,
        itemsCount: 0,
      },
    );