Search code examples
reactjsredux-toolkitrtk-query

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


I am making this shopping cart in redux-toolkit and rtk query. I want to change the cartItem index with product id.

I think the problem is with this line of code:

const cartItems = state.cartItems

My full code:

    import { createSlice, current } from '@reduxjs/toolkit';
    
    const initialState = {
        cartItems: [],
    };
    
    export const cartSlice = createSlice({
        name: 'cartSlice',
        initialState: initialState,
        reducers: {
         
            setToCart: (state, action) => {
                const { payload:product, newQty = 1 } = action;
                const cartItems = state.cartItems;
                const quantity = cartItems?.[product?._id]
                    ? parseInt(cartItems[product._id].quantity + newQty)
                    : 1;
    
                cartItems[product._id] = {
                    ...product,
                    quantity,
                };
                return {
                    ...state,
                    cartItems: cartItems
                }
            },
        },
    });
    
    export const {setToCart} = cartSlice.actions;
    export default cartSlice.reducer;

Here is action.payload:

{
    img: "_OCT2HGtyHbioAuVTMGcA-mauntain.jpg",
    name: "iphone",
    price: 600001,
    _id: "60d375ed3224711bc0f3538a"*
}

Solution

  • As the error states, when using an Immer-powered reducer you must Either return a new value or modify the draft.

    You are modifying the cartItems array. You do not need to return anything from your reducer. Just modify the values that you want to change. That's the magic of Redux Toolkit! (powered by Immer).


    There are some other issues with your reducer:

    • action will not have a property newQty. It will only have a type and payload. Perhaps newQty is a property of the payload?
    • It seems like cartItems is a dictionary keyed by _id, but you initialize it as an empty array [] instead of an empty object {}.
    • parseInt doesn't make sense outside of an addition statement. parseInt("2" + "2") will be 22, not 4.

    A "fixed" version might look like this, depending on how you structure your payload:

    import { createSlice } from '@reduxjs/toolkit';
    
    const initialState = {
        cartItems: {},
    };
    
    export const cartSlice = createSlice({
        name: 'cartSlice',
        initialState: initialState,
        reducers: {
            setToCart: (state, action) => {
                const { payload } = action;
                const { product, newQty = 1 } = payload;
                const cartItems = state.cartItems;
                // If already in cart, increase quantity.
                if (cartItems[product._id]) {
                    cartItems[product._id].quantity += newQty;
                }
                // Otherwise add to cart.
                else {
                    cartItems[product._id] = {
                        ...product,
                        quantity: newQty
                    }
                }
                // Don't return anything.
            },
        },
    });
    
    export const {setToCart} = cartSlice.actions;
    export default cartSlice.reducer;