6

I don't know why even I have modified the draft, immer didn't provide me with a new state, but the old state instance with modified content.

import CartItem from "../../models/cart-item";
import * as actions from "../actions/actionTypes";
import produce from "immer"


const initialState = [];

const reducer = (state=initialState, action) => {
    switch (action.type) {
        case actions.ADD_TO_CART:
          const {id, title, price} = action.product;
          const itemIndexInCart = state.findIndex(item => item.id === id);

          const nextState = produce(state, draftState => {
            if (itemIndexInCart === -1) {
              draftState.push(new CartItem(id, 1, price, title, price));
            } else {
              draftState[itemIndexInCart]['quantity']++;
              draftState[itemIndexInCart]['sum'] += price;
            }
          });
          console.log(state === nextState);
          return nextState;
        default:
            return state;
    }
};

export default reducer;

It is working when new item is pushing into the array, but it is not working as expected when I want to modify the quantity and sum of the array item. The log show me it's the same object under that circumstances.

Daniel Chan
  • 343
  • 5
  • 13
  • Maybe you should find the item id from the `draftState` within `produce` callback. In `produce` callback, try `const itemIndexInCart = draftState.findIndex(item => item.id === id);` right before the condition – Maxime Helen Aug 01 '20 at 02:40
  • No, it didn't work, actually the nextState is updated, but somehow it share the memory reference to the state. – Daniel Chan Aug 01 '20 at 02:46

2 Answers2

2

The trick is to make your class immerable, according to the docs. Notice that console.logging a draft might print "null", even though this object is not null.

import {immerable} from "immer"

class Foo {
    [immerable] = true // Option 1

    constructor() {
        this[immerable] = true // Option 2
    }
}

Foo[immerable] = true // Option 3

Working demo: https://codesandbox.io/s/blissful-resonance-v6v9b?file=/src/App.js

Rodrigo Amaral
  • 1,324
  • 1
  • 13
  • 17
1

Note that you should never put non-serializable values like class instances into the Redux store.

In addition, you should be using our official Redux Toolkit package, which will let you simplify your existing Redux logic. For example, this reducer could be written as:

const cartSlice = createSlice({
  name: "cart",
  initialState: [],
  reducers: {
    addToCart(state, action) {
      const {id, title, price} = action.payload;
      const item = state.find(item => item.id === id);
      
      if(item) {
        item.quantity++;
        item.sum += price;
      } else {
        state.push({id, price, title, quantity: 1});
      }
    }
  }
})
markerikson
  • 63,178
  • 10
  • 141
  • 157