Возврат нового состояния редукса с измененными свойствами объекта - PullRequest
0 голосов
/ 12 февраля 2019

Когда пользователь добавляет тот же продукт в корзину, вместо создания нового объекта с такими же свойствами в состоянии, я пытаюсь увеличить свойство объекта quantity того же продукта в состоянии.Поскольку состояние не должно быть изменено напрямую, я создаю копию состояния, редактирую quantity, а затем возвращаю новый массив.Но я получаю "Error trying to diff '[object Object]'. Only arrays and iterables are allowed".Это правильный "избыточный" способ сделать это?Ниже приведен мой случай добавления в корзину в корневом редукторе.

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 голосов
/ 12 февраля 2019

Прежде всего, когда вы используете 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 это один хороший способ.Вот справочник по избыточным состояниям:

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

Если вам удобно использовать сторонние библиотеки, в lodash есть метод cloneDeep, который будет выполнять копирование глубоких уровней дляВы:

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

})

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

Пожалуйста, не стесняйтесь комментировать, если у вас есть что-то неясное:)

0 голосов
/ 12 февраля 2019

Возможно, вы хотите изменить корзинуПродукты новым количеством.Простая петля через телепродукты сделает свое дело.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...