返回具有已编辑对象属性的新Redux状态

时间:2019-02-12 06:18:22

标签: javascript angular reactjs redux

当用户将同一产品添加到购物车中时,我试图在该状态下增加同一产品的对象属性quantity,而不是在状态下创建具有相同属性的新对象。由于状态不应该直接突变,因此我将创建状态的副本,编辑quantity,然后返回新数组。但是我得到"Error trying to diff '[object Object]'. Only arrays and iterables are allowed"。这是正确的“ redux”方法吗?下面是我在根减速器中添加到购物车的情况。

export function rootReducer(state, action): IAppState {
  switch(action.type) {
    case ADD_PRODUCT:
        return Object.assign({}, state, {
            products: state.products.concat( Object.assign({}, action.payload) )
        });
    case REMOVE_PRODUCT:
        return Object.assign({}, state, {
            products: state.products.filter(t => t.id !== action.payload)
        });
    case REMOVE_ALL_PRODUCTS:
        return Object.assign({}, state, {
            products: []
        });
    case ADD_TO_CART:
        let duplicateItem = state.cartProducts.filter(item => item.id === action.payload.id);
        let duplicateIndex;
        if(duplicateItem.length) {
            for(var i = 0; i <= state.cartProducts.length - 1; i++){
                if(state.cartProducts[i].id === duplicateItem[0].id){
                    duplicateIndex = i;   
                }
            }
            let newArray = {...state.cartProducts};
            newArray[duplicateIndex].quantity = newArray[duplicateIndex].quantity === undefined ? 1 : newArray[duplicateIndex].quantity++;
            return Object.assign({}, state, {
                cartProducts: newArray
            });
        }
        return Object.assign({}, state, {
            cartProducts: state.cartProducts.concat( Object.assign({}, action.payload.product ) )
        });
    case REMOVE_FROM_CART:
        return Object.assign({}, state, {
            cartProducts: state.cartProducts.filter(t => t.id !== action.payload)
        });
    case CLEAR_CART:
        return Object.assign({}, state, {
           cartProducts: [] 
        });
  }
  return state;
}

下面是接口

export interface IProduct {
  id: string;
  name: string;
  price: string;
  category: string;
  quantity?: number;
}
export interface IAppState {
  products: IProduct[];
  cartProducts: IProduct[];
}

2 个答案:

答案 0 :(得分:0)

您可能希望用新的数量修改cartProducts。 通过遍历cartproducts的简单循环即可解决问题。

case ADD_TO_CART : return Object.assign({}, state, { cartProducts: state.cartProducts.map((item, index) => (item.id === action.payload.id) ? item.quantity++ : item.quantity = 1) })

答案 1 :(得分:0)

首先,当您使用spread operator时,它只是为第一级创建新的引用。但是,第二级指向相同的参考。 例如,

    let collection = [
      { 
        someData: 1
      }
    ]

    let secondCollection = collection.map(item =>
      item.someData = 2
    )

console.log(collection) //[{someData: 2}]
console.log(secondCollection) //[{someData: 2}]

在这种情况下,您还必须复制第二级。使用Spread运算符,例如

let secondCollection = collection.map(item => {
  let newItem = {
    ...item
  }

  newItem.someData: 2
  return newItem
})

console.log(collection) //[{someData: 1}]
console.log(secondCollection) //[{someData: 2}]

这可能令人沮丧,并且有许多方法可以减轻麻烦。 Normalization是一种好方法。这是redux状态的参考:

https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape

如果您愿意使用第三方库,则cloneDeep中有lodash种方法可以为您复制深层次:

https://lodash.com/docs/4.17.11#cloneDeep

对于您的更新购物车:

可以简化很多。绝对没有必要将重复项放入数组中,并且for遍历整个循环。

让我们尝试简化它:

//assuming the carProducts state as
[
  {
    id: 1,
    quantity: 2,
  },
  {
    id: 2,
    quantity: 2,
  }
]

let newState = carProducts.map(item => {
  let newItem = {...item} //or _.cloneDeep(item)

  newItem.quantity = item.quantity + 1 //or add up the payload

  return newItem

})

您还应该在不同的reducer中分离产品,carProducts等状态,以简化操作并增强控制力。

如果您有不清楚的地方,请随时发表评论:)