Search code examples
reactjsredux-toolkit

Error: [Immer] An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft


So, I am making this shopping cart in redux-toolkit, where user can't order more than the quantity available. So, my reducer function is like below for adding to cart.

import { createSlice } from "@reduxjs/toolkit";
import { FoodCartType } from "../../types";

type CartState = {
  cart: FoodCartType[];
};

const initialState: CartState = {
  cart: [],
};

export const foodcartSlice = createSlice({
  name: "foodcart",
  initialState,
  reducers: {
    addToCart: (state, action) => {
      console.log(action);
      if (state.cart.length === 0) {
        state.cart.push(action.payload);
      }
      const itemIndex = state.cart.findIndex(
        (item) => item.id === action.payload.id
      );

      if (itemIndex >= 0) {
        return {
          ...state,
          cart: state.cart.map((item) => {
            if (item.id === action.payload.id) {
              return {
                ...item,
                quantity: item.quantity + 1,
                quantity_available: item.quantity_available - 1,
              };
            }
          }),
        };
      } else {
        return {
          ...state,
          cart: [
            ...state.cart,
            {
              ...action.payload.product,
              quantity: 1,
              quantity_available: action.payload.quantity_available - 1,
            },
          ],
        };
      }
    },
  },
});

export const { addToCart } = foodcartSlice.actions;

export default foodcartSlice.reducer;

When I click on the dispatch, on the component. I get the following error.

Unhandled Runtime Error Error: [Immer] An immer producer returned a new value and modified its draft. Either return a new value or modify the draft.

So, where I am doing the problem? if I just remove the itemIndex check part, items get pushed into the cart. But, other than that it is giving me this error. How should I rewrite this?


Solution

  • Because your state.cart.push(action.payload); line modifies the value of state and later you return a new value.

    You are allowed to do one, but not both at once. See writing reducers with immer

    What you could do here: always modify. That's also far more readable.

    export const foodcartSlice = createSlice({
      name: "foodcart",
      initialState,
      reducers: {
        addToCart: (state, action) => {
          console.log(action);
    // you push later anyways, so you can also delete these lines
    //      if (state.cart.length === 0) {
    //        state.cart.push(action.payload);
    //      }
          const itemIndex = state.cart.findIndex(
            (item) => item.id === action.payload.id
          );
    
          if (itemIndex >= 0) {
            state.cart[itemIndex].quantity += 1
            state.cart[itemIndex].quantity_available -= 1
          } else {
            state.cart.push({
                  ...action.payload.product,
                  quantity: 1,
                  quantity_available: action.payload.quantity_available - 1,
            })
          }
        },
      },
    });