为什么它被认为是直接修改 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
是一个嵌套对象,因此在浅拷贝之后,它仍然指向与原始状态相同的内存地址
关于代码片段的信息: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
是一个嵌套对象,因此在浅拷贝之后,它仍然指向与原始状态相同的内存地址