Почему это считается прямой модификацией значений магазина в NgRx? - PullRequest
1 голос
/ 19 апреля 2020

Информация о фрагменте: состояние имеет фрагмент educationList, который представляет собой массив различных образований (BS, MS, PhD). Каждый элемент в массиве educationList содержит другой массив description, который содержит информацию для проектов, thesis et c.

Ниже приведен фрагмент для удаления одного из элементов массива 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)

Но я думаю, что уже скопировал состояние в самой 1-й строке.

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

Чего мне здесь не хватает?

1 Ответ

1 голос
/ 19 апреля 2020
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 является вложенным объектом, поэтому после поверхностной копии он все еще указывает на тот же адрес в памяти, что и в исходном состоянии

...