1

I am having some trouble understanding why I am getting an error message:

TypeError: Cannot assign to read only property 'description' of object '#'

I know that the principle is that I don't want to modify state in my reducer. Instead, I want to return a new copy of the state.

Here is my reducer:

action TOGGLE_CHECKBOX:
    {
        let copyOfItems = [...state.items]; // create a new array of items

        copyOfItems.forEach(i => i.description = "newDescription");

        // return a new copy of the state
        return {
            ...state,
            items: copyOfItems
        }
    }

And here is my Reducer test:

it ('Test that each item description is set', () => {
    const state = {
        items: [
            { description: "d1" },
            { description: "d2" }
        ]
    }

    deepFreeze(state);

    expect(MyReducer(state, { type: TOGGLE_CHECKBOX })).toEqual({
        items: [
            { description: "newDescription" },
            { description: "newDescription" }
        ]
    });
});

However, I get the above error message. If I remove the deepFreeze the test passes. This means that I am somehow modifying the original state in some way but I can't figure out why especially because I created a new array of spreaded items.

Any help would be greatly appreciated.

Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
noblerare
  • 10,277
  • 23
  • 78
  • 140
  • Possible duplicate of [Whats the best way to update an object in an array in ReactJS?](https://stackoverflow.com/questions/28121272/whats-the-best-way-to-update-an-object-in-an-array-in-reactjs) – Emile Bergeron Sep 05 '19 at 20:10
  • Other similar question: [How to update nested state properties in React](https://stackoverflow.com/q/43040721/1218980) – Emile Bergeron Sep 05 '19 at 20:13
  • Other possible duplicate: [copy array of objects and make changes without modifying original array](https://stackoverflow.com/q/45512723/1218980) – Emile Bergeron Sep 05 '19 at 20:19

2 Answers2

7

The array spread operator makes a shallow copy of the state.items array, but does not make a copy of the objects inside of that array. In order to get a new array with modified items you can map over state.items and use the object spread operator to update the items:

action TOGGLE_CHECKBOX:
    {
        const copyOfItems = state.items.map(
          i => ({...i, description: 'newDescription'})
        ); // create a new array of items with updated descriptions

        // return a new copy of the state
        return {
            ...state,
            items: copyOfItems
        }
    }
azundo
  • 5,902
  • 1
  • 14
  • 21
0

The spread operator makes shallow copy of the array which means the object inside the array will still hold the reference to the original values. You need to make a new copy for each object and then update the description for each like this

let copyOfItems = state.items.map( obj => ({
  ...obj,
  description: "newDescription"
})); 

return {
  ...state,
  items: copyOfItems
}

Hope this helps !

Hemant Parashar
  • 3,684
  • 2
  • 16
  • 23