Мутирует ли я состояние редуктора в редукторе? - PullRequest
0 голосов
/ 18 сентября 2018

Я пытаюсь изменить один элемент массива в моем состоянии, используя редуктор.

Состояние выглядит так:

state: {
  items: [
  {
    id: 1,
    name: 'Superman',
    wearsCape: true
  },
  {
    id: 2,
    name: 'Batman',
    wearsCape: true
  },
  {
    id: 3,
    name: 'Iron Man',
    wearsCape: false
  }
];
}

Я пытаюсь отфильтровать все элементы в состоянии, чтобы найти первое вхождение супергероя с именем, равным sHero.name. Тогда я пытаюсь изменить свойство найденного супергероя. Код редуктора выглядит так:

function findCapedCrusader(state, { sHero }) {
  var result = state.items.filter(s => {
    return s.name === sHero.name;
  });

  result[0].wearsCape = false;

  return { ...state };

}

Я мутирую состояние, выполняя вышеуказанное ??

ПРИМЕЧАНИЕ: извиняюсь за глупый пример. Я новичок в реакции и избыточности.

Ответы [ 4 ]

0 голосов
/ 18 сентября 2018

Насколько я знаю, метод filter возвращает новый массив, но возвращаемые элементы по-прежнему ссылаются на старые. Таким образом, если кто-то изменяет свойство во вновь созданном массиве, он также изменяет исходное.

const state = {
  items: [
  {
    id: 1,
    name: 'Superman',
    wearsCape: true
  },
  {
    id: 2,
    name: 'Batman',
    wearsCape: true
  },
  {
    id: 3,
    name: 'Iron Man',
    wearsCape: false
  }
]
};

const newItem = state.items.filter( s => s.name === "Superman");

newItem[0].wearsCape = false;

console.log( "newitem", newItem, "\noriginal", state.items[0] );

Если вы хотите изменить свойство одного объекта в массиве и обновить свое состояние:

const state = {
  items: [
  {
    id: 1,
    name: 'Superman',
    wearsCape: true
  },
  {
    id: 2,
    name: 'Batman',
    wearsCape: true
  },
  {
    id: 3,
    name: 'Iron Man',
    wearsCape: false
  }
]
};

const newItems = state.items.map( s => {
  if( s.name === "Superman") {
    return { ...s, wearsCape: false };
  }
  return s;
});

console.log( "new items", newItems, "original", state.items );

Как видите, оригинал items не является мутированным. Для вашей ситуации это будет примерно так:

function findCapedCrusader(state, { sHero }) {
  var newItems = state.items.map(s => {
    if (s.name === sHero.name ) {
        return { ...s, wearsCape = false }
    }
    return s;
  });
  return { ...state, items: newItems };
}

Опять же, эта функция изменит свойство одного объекта и больше ничего не изменит. Я не совсем уверен, что это то, что вы хотите, так как вы пытаетесь набрать filter в своем вопросе. Если вы хотите обновить свое состояние одним объектом, я могу предложить другое решение.

0 голосов
/ 18 сентября 2018

Да, вы изменяете свое состояние в этом примере. Хотя filter создает новый массив, на самом деле это только поверхностная копия оригинала. Элементы вашего массива state.items являются объектами, поэтому, когда вы изменяете один из них в своем новом массиве results (который содержит указатели на исходное содержимое state.items), вы изменяете исходный объект. Посмотрите этот ответ немного подробнее о глубоких и мелких копиях: В чем разница между мелкой копией и глубокой копией с массивами JavaScript?

Чтобы исправить это, вы можете:

1. Используйте оператор распространения на result, а затем перезапишите значение wearsCape следующим образом:

result = state.items.filter(s => s.name === sHero.name).map(s => {
    return {...s, wearsCape: false}
});

Этот метод работает с сериализуемыми и несериализуемыми данными и является рекомендуемым способом обновления значений вложенных состояний.

Или

2. Сделайте глубокую копию state.items перед фильтрацией:

const results = JSON.parse(JSON.stringify(state.items)).filter(s => {
    return s.name === sHero.name;
});

Этот метод немного странный, но хорошо работает для сериализуемых данных в тех случаях, когда значение, которое вы хотите изменить, глубоко вложено, и использование оператора распространения (...) много раз становилось бы громоздким.

0 голосов
/ 18 сентября 2018

Вы есть.Даже если функция фильтра создаст новый массив, состояние находится в объектах, которые вы возвращаете в этом новом массиве.Исходя из этого, вы меняете состояние, которое не соответствует тому, что является чистой функцией.

Здесь я оставляю вам хороший пост, который поможет вам лучше понять, что такое глубокая копия и как ее выполнить.

https://medium.com/@tkssharma/objects-in-javascript-object-assign-deep-copy-64106c9aefab

0 голосов
/ 18 сентября 2018

Метод filter() возвращает новый экземпляр массива, поэтому вы не изменяете свое состояние.Для получения дополнительной информации см. Документацию MDN :

Возвращаемое значение

Новый массив, каждый элемент которого является результатом функции обратного вызова.

...