Мутация состояния Redux, несмотря на создание копии состояния с оператором распространения - PullRequest
2 голосов
/ 26 мая 2020

Почему я получаю предупреждение о мутации состояния, когда делаю копию объекта с помощью оператора распространения? Я пытаюсь сделать тележку с редукцией. При добавлении элемента или уменьшении его количества я отправляю действие, обновляющее его состояние. В моем редукторе, если продукт еще не добавлен, я возвращаю новую копию массива состояний с новым объектом. Если он уже добавлен, я выполняю итерацию с map (которая возвращает новый массив) и с копией продукта, который я увеличиваю, и его свойство qty увеличено. Проблема с этим свойством. Как правильно обновить это свойство без изменения состояния?

reducer

case types.ADD_TO_CART_SUCCESS:
  const isAdded = state.cart.find(item => item.sku === action.product.sku);
  if (!isAdded) action.product.qty = 1;
  return {
    ...state,
    cart: !isAdded
      ? [action.product, ...state.cart]
      : state.cart.map(item =>
          item.sku === action.product.sku
            ? { ...item, qty: ++item.qty }
            : item
        ),
  };

Предупреждение о нарушении

Invariant Violation: A state mutation was detected inside a dispatch, in the path: `user.cart.0.qty`. Take a look at the reducer(s) handling the action {"type":"ADD_TO_CART_SUCCESS"....}

Ответы [ 3 ]

3 голосов
/ 26 мая 2020

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

case types.ADD_TO_CART_SUCCESS:
  const isAdded = state.cart.find(item => item.sku === action.product.sku);
  if (!isAdded) action.product.qty = 1;
  return {
    ...state,
    cart: !isAdded
      ? [action.product, ...state.cart]
      : state.cart.map(item =>
          item.sku === action.product.sku
            ? { ...item, qty: ++item.qty } // <-- This mutates the current item.qty
            : item
        ),
  };

Однако вы можете вернуть увеличенное значение, текущее item.qty + 1

case types.ADD_TO_CART_SUCCESS:
  const isAdded = state.cart.find(item => item.sku === action.product.sku);
  if (!isAdded) action.product.qty = 1;
  return {
    ...state,
    cart: !isAdded
      ? [action.product, ...state.cart]
      : state.cart.map(item =>
          item.sku === action.product.sku
            ? { ...item, qty: item.qty + 1 }
            : item
        ),
  };

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

1 голос
/ 27 мая 2020

Похоже, вы используете наш официальный пакет Redux Toolkit , который по умолчанию предупреждает о случайных мутациях состояния. Это здорово, потому что в данном случае явно обнаружена мутация.

Но, что еще лучше, Redux Toolkit использует Immer внутри, чтобы вы могли писать «мутирующие» обновления состояния в редукторах! . Это означает, что ваш редуктор logi c можно упростить до этого:

const cartSlice = createSlice({
  name: 'cart',
  initialState,
  reducers: {
    addToCartSuccess(state, action) {
      const item = state.cart.find(item => item.sku === action.payload.product.sku);
      if (item) {
        item.qty++;
      } else {
        action.product.qty = 1;
        state.cart.unshift(action.payload.product);
      }
    }
  }
})
0 голосов
/ 27 мая 2020

Я делал что-то подобное пару раз, попробуйте этот код:

const addItemToCart = (cartItems, cartItemToAdd) => {
  const existingCartItem = cartItems.find(
    (cartItem) => cartItem.id === cartItemToAdd.id
  );

  if (existingCartItem) {
    return cartItems.map((cartItem) =>
      cartItem.id === cartItemToAdd.id
        ? { ...cartItem, quantity: cartItem.quantity + 1 }
        : cartItem
    );
  }

  return [...cartItems, { ...cartItemToAdd, quantity: 1 }];
};
...