为什么它被认为是直接修改 NgRx 中的存储值

Why is it considered a direct modification of store values in NgRx

关于代码片段的信息:state 有一个切片 educationList,它是各种教育(BS、MS、PhD)的数组。 educationList 数组中的每个项目都包含另一个数组 description,其中包含项目、论文等信息。

以下是删除 description 数组中的一项的代码段:

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 };
    }

如果代码段中有以下行,

updatedEducationList[targetIndex] = { ...updatedEducationList[targetIndex], description: newDescription }

替换为

updatedEducationList[targetIndex].description = newDescription;  //Edited this line

发生错误。错误如下。

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)

但我想我已经复制了第一行中的状态。

let updatedEducationList = [...state.educationList];

我在这里错过了什么?

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 };

回答

对于包含其他对象或数组的对象和数组,复制这些对象需要深拷贝。否则,对嵌套引用所做的更改将更改嵌套在原始对象或数组中的数据。在这种情况下description是一个嵌套对象,因此在浅拷贝之后,它仍然指向与原始状态相同的内存地址