React redux toolkit: Uncaught Error:[Immer] An immer producer returned a new...

ruoyxue發表於2023-04-17

React在寫一個購物車的redux toolkit時遇到了問題。核心程式碼如下:

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

const cartSlice = createSlice({
    name: 'cart',
    initialState: {
        cartItems: [],
        cartItemCount: 0
    },
    reducers: {
        addProduct(state, action) {
            const { imageUrl, name, price } = action.payload
            let newCartItems = [...state.cartItems]

            let flag = newCartItems.some((item, index, arr) => {
                if(item.name == name) {
                    arr[index].quantity += 1
                    return true
                }
            })

            if(!flag) {
                newCartItems = [ 
                    ...state.cartItems,
                    { imageUrl, name, price, quantity: 1 } 
                ]
            }

            const cartItemCount = newCartItems.reduce(
                (prevTotal, currItem) =>  prevTotal + currItem.quantity, 0
            )
            return { cartItems: newCartItems, cartItemCount }
        }
    }
})

export const { addProduct, deleteProduct, incQuantity, decQuantity } = cartSlice.actions
export default cartSlice.reducer

核心邏輯是,呼叫addProduct來修改redux所管理的cartItems和cartItemCount,當向購物車中新增一個新的物品時,程式碼正常執行;但當反覆新增一個物品時,程式碼報出如下錯誤:

image.png

搜下資料發現是因為redux使用immer,不允許在reducer中修改state值後還return,二者只能取其一。

因此,當新增一個新物品時,由於沒有修改cartItems,因此可以有返回值,不報錯。

但當新增一個重複的物品時,即使在最開始使用了let newCartItems = [...state.cartItems]來建立一個新的物件,但由於state.cartItems陣列中儲存的不是基本資料型別,而是一個物件,因此newCartItems儲存的是state.cartItems中各個物件的引用,即

newCartItems == state.cartItems  // false
newCartItems[0] == state.cartItems[0]  // true

由此可得,新增一個新物品時,由於既修改了state,又return了新值,因此報出immer錯誤。

修改後的程式碼如下:

addProduct(state, action) {
    const { imageUrl, name, price } = action.payload

    let flag = state.cartItems.some((item, index, arr) => {
        if(item.name == name) {
            arr[index].quantity += 1
            state.cartItemCount += 1
            return true
        }
    })

    if(!flag) {
        state.cartItems.push({ imageUrl, name, price, quantity: 1 })
        state.cartItemCount += 1
    }
}

這裡只是對state做了修改,而沒有return新值,因此程式碼正常執行。

相關文章