我是否在reducer中改变redux状态?

时间:2018-09-18 02:52:59

标签: javascript reactjs redux immutability

我正在尝试使用reducer在状态下更改数组中的一项。

状态如下:

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 :(得分:0)

filter()方法将返回一个新的数组实例,因此您不会改变状态。有关更多信息,请参见MDN documentation

  

返回值

     

一个新数组,每个元素都是回调函数的结果。

答案 1 :(得分:0)

是的,在此示例中,您正在更改状态。尽管filter确实产生了一个新数组,但实际上只是原始数组的浅表副本。 state.items数组的元素是对象,因此,当您在新的results数组(包含指向state.items的原始内容的指针)中修改一个元素时,便会修改原始对象。有关深层副本和浅层副本的更多信息,请参见以下答案:What is the difference between a shallow copy and a deep copy with JavaScript arrays?

要解决此问题,您可以:

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

这种方法有点奇怪,但是在要修改的值被深层嵌套并且多次使用扩展运算符(...)的情况下,该方法非常适用于可序列化的数据。

答案 2 :(得分:0)

你是。即使filter函数将产生一个新数组,但状态仍在该新数组中返回的对象中。基于此,您正在更改状态,该状态不符合纯功能是什么。

在这里,我给您留下了一篇不错的文章,它将帮助您更好地理解什么是深层副本以及如何完成它。

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

答案 3 :(得分:0)

据我所知,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,因此我不太确定这是您想要的。如果您想用一个对象更新状态,我可以建议另一种解决方案。