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
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
}
Don't mutate your state by pushing directly into your cartItems
array.
state.cartItems.push(cart); // <-- mutates state reference
You need to search the cartItems
first to see if you've already added a cart item.
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,
},
);