Reputation: 364
Info about the snippet: state has a slice educationList
which is an array of various educations (BS, MS, PhD). Each item in educationList
array contains another array description
, which contains info for projects, thesis etc.
The following is the snippet for deleting one of items of the description
array:
case Actions.DELETE_ROLE: {
let updatedEducationList = [...state.educationList];
const targetIndex = updatedEducationList.findIndex((educationItem) => {
return educationItem.id === action.payload.educationId;
});
let oldDescription = updatedEducationList[targetIndex].description;
let newDescription = oldDescription.filter((item, index) => index !== action.payload.roleIndex);
updatedEducationList[targetIndex] = { ...updatedEducationList[targetIndex], description: newDescription };
return { ...state, educationList: updatedEducationList };
}
if the following line in the snippet,
updatedEducationList[targetIndex] = { ...updatedEducationList[targetIndex], description: newDescription }
is replaced with
updatedEducationList[targetIndex].description = newDescription; //Edited this line
error takes place. The error is following.
core.js:6014 ERROR TypeError: Cannot assign to read only property 'description' of object '[object Object]'
at educationListReducer (education-list.reducer.ts:110)
at combination (store.js:303)
at store.js:1213
at store.js:383
at ScanSubscriber.reduceState [as accumulator] (store.js:688)
at ScanSubscriber._tryNext (scan.js:49)
at ScanSubscriber._next (scan.js:42)
at ScanSubscriber.next (Subscriber.js:49)
at WithLatestFromSubscriber._next (withLatestFrom.js:57)
at WithLatestFromSubscriber.next (Subscriber.js:49)
But I think I already copied the state in the 1st line itself.
let updatedEducationList = [...state.educationList];
What am I missing here?
Upvotes: 1
Views: 82
Reputation: 13539
let updatedEducationList = [...state.educationList];
// now updatedEducationList is a new array
// but its every element points to the related value in the current state.
// because it is not a deep clone.
const targetIndex = updatedEducationList.findIndex((educationItem) => {
return educationItem.id === action.payload.educationId;
});
// now if you check state.educationList[targetIndex] === updatedEducationList[targetIndex] it will be true.
// Despite updatedEducationList === state.educationList is false.
let oldDescription = updatedEducationList[targetIndex].description;
// now it points to the current state.
let newDescription = oldDescription.filter((item, index) => index !== action.payload.roleIndex);
// a new array of pointers to the current state items.
updatedEducationList[targetIndex].description = newDescription;
// wrong, because updatedEducationList[targetIndex] still points to the current state. and so does description.
updatedEducationList[targetIndex] = { ...updatedEducationList[targetIndex], description: newDescription };
// right, because it creates new object that doesn't point to current state anymore. only values of its keys.
return { ...state, educationList: updatedEducationList };
ANSWER
For objects and arrays containing other objects or arrays, copying these objects requires a deep copy. Otherwise, changes made to the nested references will change the data nested in the original object or array. In this case description
is a nested object, therefore after the shallow copy, it still points to the same address in the memory as that from the original state
Upvotes: 1