Home > OS >  Immer An immer producer returned a new value *and* modified its draft. Either return a new value *or
Immer An immer producer returned a new value *and* modified its draft. Either return a new value *or

Time:08-01

hi . I am planning to create a shopping cart system for my site. I use React and Redux toolkit. But when I want to dispatch my states, I encounter this error. Thank you for helping me. cartSlide (Cart Reducer) :

import {createSlice} from "@reduxjs/toolkit";

const initialState = {
    selectedItems: [],
    itemsCounter: 0,
    total: 0,
    checkout: false
}

const sumItems = items => {
    const itemsCounter = items.reduce((total , product) => total   product.quantity, 0)
    const totalPrice = items.reduce((total , product) => total   product.price * product.quantity,0).toFixed(2)
    return {
        itemsCounter,
        totalPrice
    }
}

export const cartSlice = createSlice({
    name: 'cart',
    initialState,
    reducers: {
        AddItem:(state,action)=>{
            if (!state.selectedItems.find(item => item.id === action.payload.id)) {
                   state.selectedItems.push({
                       ...action.payload,
                       quantity: 1
                   })
            }
            return {
                ...state,
                selectedItems: [...state.selectedItems],
                ...sumItems(state.selectedItems),
                checkout: false
            }
        },
        RemoveItem: (state, action) => {
            const newSelectedItems = state.selectedItems.filter(item => item.id !== action.payload.id);
            return {
                ...state,
                selectedItems: [...newSelectedItems],
                ...sumItems(newSelectedItems)

            }
        },

        Increase: (state, action) => {
            const indexI = state.selectedItems.findIndex(item => item.id === action.payload.id);
            state.selectedItems[indexI].quantity  ;
            return {
                ...state,
                ...sumItems(state.selectedItems)

            }
        },

        Decrease: (state, action) => {
            const indexD = state.selectedItems.findIndex(item => item.id === action.payload.id);
            state.selectedItems[indexD].quantity--;
            return {
                ...state,
                ...sumItems(state.selectedItems)

            }
        },
        Checkout: () => {
            return {
                selectedItems: [],
                itemsCounter: 0,
                total: 0,
                checkout: true
            }
        },
        Clear: () => {
            return {
                selectedItems: [],
                itemsCounter: 0,
                total: 0,
                checkout: false
            }
        }
    }
})

export const {AddItem,RemoveItem,Increase,Decrease,Checkout,Clear} = cartSlice.actions
export default cartSlice.reducer

The error is for the AddItem action, and when I delete the return part, the code works. this part:

 AddItem:(state,action)=>{
            if (!state.selectedItems.find(item => item.id === action.payload.id)) {
                   state.selectedItems.push({
                       ...action.payload,
                       quantity: 1
                   })
            }
            return {
                ...state,
                selectedItems: [...state.selectedItems],
                ...sumItems(state.selectedItems),
                checkout: false
            }
        },

CodePudding user response:

Don't both modify the state object state.selectedItems.push and use a return.

Option A - Modify Draft

AddItem: (state,action) => {
    // Check if item exists
    if (!state.selectedItems.find(item => item.id === action.payload.id)) {
        // add item since it did not exist.
        state.selectedItems.push({
            ...action.payload,
            quantity: 1
        })
    }

    // Calculate new count and totals.
    const sum = sumItems(state.selectedItems);

    // Apply new count and totals to the state.
    state.itemsCounter = sum.itemsCounter;
    state.totalPrice = sum.totalPrice;

    // Set checkout to false.
    state.checkout = false;
}

Option B - New State

AddItem: (state,action) => {
    // create a new array containing selected items.
    let newItems = [
        ...state.selectedItems;
    ];

    // check if item already exists
    if (!newItems.find(item => item.id === action.payload.id)) {
        // add item since it did not exist
        newState.selectedItems.push({
            ...action.payload,
            quantity: 1
        });
    }

    // return a new state
    return {
        ...state,
        selectedItems: newItems,
        ...sumItems(newItems),
        checkout: false
    }
}

You should probably stick with modifying the draft method unless you intend to replace the state with a completely new state. Using the draft method Immer should handle the reference updates where needed and allows you to write code as if you were editting a normal mutable object (for the most part, see the docs for some limitations).

  • Related