Невозможно добавить существующий продукт в корзину в редукторе в редукторе с реагированием на родной

Я работаю над собственным приложением реакции, у которого есть материнский магазин, я использую redux для работы с глобальным состоянием, и в моих редукторах мне нужно обрабатывать добавление в корзину, если продукт не существует в корзине или если продукт существует в тележка, моя цель - просто увеличить его количество без увеличения общего количества товаров вот мои действия и редукторы

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
}

мои редукторы для тележки

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;

первый случай в редукторе для добавления в корзину, когда itemsCount === 0 работает, однако, когда в корзине более одного элемента, редуктор не работает должным образом, и мне нужна помощь


person joshington    schedule 24.02.2021    source источник


Ответы (1)


Проблемы

  1. Не храните значения itemsCount и cartTotalAmount в состоянии, они легко выводятся из данных состояния. Хранение повторяющихся или производных данных - это антипаттерн.

    const initialState = {
      itemsCount : 0, // <-- easily computed from cart items
      cartItems:[],
      cartTotalAmount:0, // <-- easily computed from cart items
    }
    
  2. Не изменяйте свое состояние, вставляя его прямо в массив cartItems.

    state.cartItems.push(cart); // <-- mutates state reference
    
  3. Сначала вам нужно выполнить поиск в cartItems, чтобы увидеть, добавили ли вы уже товар в корзину.

Решение

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,
        }
      ],
    }
  }

  ...

Вы можете применить аналогичные шаблоны обновления для других типов действий, которые обновляют элементы корзины.

Чтобы вычислить itemsCount в пользовательском интерфейсе

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

Чтобы вычислить cartTotalAmount

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

Их можно объединить в один расчет за один проход.

const { cartTotalAmount, itemsCount } = cartItems.reduce(
  ({ cartTotalAmount, itemsCount }, { price, quantity }) => ({
    cartTotalAmount: cartTotalAmount + quantity * price,
    itemsCount: itemsCount + quantity,
  }),
  {
    cartTotalAmount: 0,
    itemsCount: 0,
  },
);
person Drew Reese    schedule 24.02.2021
comment
спасибо, позвольте мне попробовать, хотя следует ли мне поместить cartTotalAmount и itemsCount в один и тот же редуктор, поскольку в конечном итоге мне придется использовать их в глобальном состоянии моего приложения - person joshington; 25.02.2021
comment
@joshington Нет, они являются производным состоянием, вы должны создать функцию выбора состояния, чтобы читать это состояние и вычислять значения. Используйте с useSelector или mapStateToProps. - person Drew Reese; 25.02.2021