如何编辑状态数组中的项目?

39

所以这就是我的状态:

this.state = {
  ids: ['A', 'E', 'C']
};

如何修改状态,以便将索引1处的'E'更改为'B'呢?例如:

this.setState({
  ids[1]: 'B'
});

如何完成这个任务?


2
今天 Array.prototype.map() 是你最好的朋友,可以在这里看看:https://www.robinwieruch.de/react-state-array-add-update-remove - Cris69
6个回答

80

我的建议是逐渐习惯使用不可变操作,这样您就不会修改内部状态对象。

正如在React文档中所指出的:

永远不要直接改变 this.state 的值,因为调用 setState() 之后可能会替换您所做的更改。将 this.state 视为不可变

在这种情况下,您可以[1]使用slice()获取数组的新副本[2]对副本进行操作,然后[3]使用新的数组调用setState。这是一个好习惯。

就像这样:

const newIds = this.state.ids.slice() //copy the array
newIds[1] = 'B' //execute the manipulations
this.setState({ids: newIds}) //set the new state

我认为这行代码的意思是:this.state.a='a',不要直接更改任何状态变量的值。this.state 当作不可变的对象来处理,始终使用 setState 来更改状态值。如果我理解有误,请纠正我。 - Mayank Shukla
在JavaScript中,数组是对象,因此它们是通过“引用”传递的(如果您使用新字符串设置状态键,则没有问题)。但是,从this.state修改相同的数组/对象将改变相同的内部对象。 - mrlew
1
谢谢您。我花了太多时间想知道为什么React没有看到只更新带有修改后的数组副本的状态更改。直到我使用slice()制作该副本,它才看到状态更改。 - Dave
3
我花了一个小时搜寻才找到一个简单明了的解释来完成这个操作。复制该数组并编辑此副本,然后从副本中更新状态(setState)。很简单。谢谢您的帮助! - user1057001

36

情况1:如果您知道索引,则可以这样写:

let ids = [...this.state.ids];     // create the copy of state array
ids[index] = 'k';                  //new value
this.setState({ ids });            //update the value

第二种情况:如果您不知道索引,则首先使用array.findIndex或者任何其他循环来获取要更新的项的索引,然后更新该值并使用setState。

像这样:

let ids = [...this.state.ids];  
let index = ids.findIndex(el => /* condition */);
ids[index] = 'k';                  
this.setState({ ids });            

6

这里有另一种解决方案可以在setState中更改特定索引的数组:

this.setState({
  ...array,
  Object.assign([...array], { [id]: yourNewObjectOrValue })
})

2

在@mayank-shukla所写的基础上(情况2:知道要替换的项的索引),也可以使用Array.splice来编写:

const replacement = 'B';
let copy = [...this.state.ids]
copy.splice(index, 1, replacement)

this.setState({ 
   ids: copy,
})

REPL 示例

这里有两件事需要注意:

  1. Array.splice 是可变的;它会更改它操作的数组,但由于展开运算符,这是一个浅拷贝的数组。请参见下面的更多信息。
  2. 您无法直接分配剪辑的结果,因为 Array.splice 的返回值实际上是删除的元素。即:不要将切片结果分配给您打算分配给 setState 中的 IDs 变量,否则您最终只会得到已删除的值。

关于第一点中的浅复制和深复制,请注意,如果您正在替换对象引用(而不是问题中的字符串文本),则需要使用类似于 lodash 的 cloneDeep

还有其他一些 解决方法

您还可以在 SO 上 阅读更多关于浅复制和深复制的内容。


你不能直接赋值 splice 的结果,因为 Array.splice 的返回值实际上是被删除的元素。也就是说,不要将你的 slice 结果分配给你打算在 setState 中赋值的变量,否则你最终只会得到已删除的值。你的示例不就是这样做的吗? - Aaron
1
@Aaron 在帖子中是的。在 REPL 中不行。我会更新帖子本身。 - zedd45

1
this.setState({
  ids: [ids[1]='B',...ids].slice(1)
});

上述代码将创建两个值为“B”的数组项,一个位于开头,另一个位于指定位置。 只需使用切片运算符删除第一个数组元素即可。
另一种解决方案是直接使用splice运算符。
this.setState({
  ids: [ids.splice(1,1,'B')]
})

1
我知道这个问题很老,但今天有更好的答案。 immer可以用来支持一种更合理的改变不可变对象的方式,并提供熟悉的API。通过将函数传递给this.setState,您可以使用它来转换状态。
this.setState(state => produce(state, state => {
  state.ids[1] = 'b'
}));

或者,如果你更喜欢柯里化,produce(fn)是一个有用的快捷方式,可以创建一个函数,接受一个参数并将其转发到你的变换中:

this.setState(produce(state => {
  state.ids[1] = 'b'
}));

我发现当涉及到深层状态更改时,Immer非常好用,但请注意它并非免费。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接