在组件状态下从数组中删除元素


131

我试图找到在组件状态下从数组中删除元素的最佳方法。由于我不应该this.state直接修改变量,是否有比我在这里有更好的方法(更简洁)从数组中删除元素?:

  onRemovePerson: function(index) {
    this.setState(prevState => { // pass callback in setState to avoid race condition
      let newData = prevState.data.slice() //copy array from prevState
      newData.splice(index, 1) // remove element
      return {data: newData} // update state
    })
  },

谢谢。

更新

这已更新为在setState中使用回调。在更新当前状态时,应执行此操作。


看看来自Facebook的ImmutableJS,它可以与React很好地配合使用。链接
Jonatan LundqvistMedén2015年

6
我看不到您的代码有什么问题。实际上,这是一种非常惯用的方法。
Dimitar Dimitrov 2015年

Answers:


138

我见过的最干净的方法是filter

removeItem(index) {
  this.setState({
    data: this.state.data.filter((_, i) => i !== index)
  });
}

18
@HussienK _有时用于表示未使用的参数。在这里,它是数组中的当前项目。
chrisM

1
惊人。我一直在使用.filter,但从未考虑过在这种情况下使用它。:D
dakt

4
这是干净的代码,但是,对于大型数组,此方法很慢。原因是,它遍历整个数组以查找看起来已经确定的索引。
马特·埃利斯

1
@MattEllis由于React的状态更新始终是不可变的,因此无论如何都要复制列表大小的O(n)。如果这对性能造成重大影响,我会感到惊讶。
ephrion

4
不建议this.state将评估作为输入this.setState(),甚至是一成不变的。请参阅上面的答案,以获取有关该主题的官方React文档的参考。
pscl

87

您可以使用update()fromreact-addons-update不变性帮助器,该帮助器实际上可以在后台执行相同的操作,但是您可以做的很好。

this.setState(prevState => ({
  data: update(prevState.data, {$splice: [[index, 1]]})
}))

比您链接到的示例简单得多的示例:)
Koen。

7
react-addons-update现在已弃用(2016)。immutability-helper可作为替代品。github.com/kolodny/immutability-helper 也请参见下面有关不直接在this.setState()内部对this.state进行突变的答案。
pscl

62

我认为不建议this.state在内部引用setState()状态更新可能是异步的)。

文档建议setState()与回调函数一起使用,以便在更新发生时在运行时传递prevState。因此,其外观如下:

在不使用ES6的情况下使用Array.prototype.filter

removeItem : function(index) {
  this.setState(function(prevState){
    return { data : prevState.data.filter(function(val, i) {
      return i !== index;
    })};
  });
}

将Array.prototype.filter与ES6箭头函数一起使用

removeItem(index) {
  this.setState((prevState) => ({
    data: prevState.data.filter((_, i) => i !== index)
  }));
}

使用不变性帮助器

import update from 'immutability-helper'
...
removeItem(index) {
  this.setState((prevState) => ({
    data: update(prevState.data, {$splice: [[index, 1]]})
  }))
}

使用价差

function removeItem(index) {
  this.setState((prevState) => ({
    data: [...prevState.data.slice(0,index), ...prevState.data.slice(index+1)]
  }))
}

请注意,在每种情况下,无论使用何种技术,this.setState()都会传递一个回调,而不是对old的对象引用this.state


3
这是正确的答案,另请参见不变异数据的力量
Vinnie James

1
使用prevState回调并添加了各种技术的示例。
pscl,

如果我这样做怎么办? this.setState({ data: [...this.state.data.slice(0, index), ...this.state.data.slice(index + 1)] }); 使用this.state而不是prevState@ user1628461显示的回调选项是错误的吗?
Tiberiu Maxim

这是最好的解决方案,尽管不是最简单的方法
Developia

谢谢,使用spread有可能也在中间更新元素而不是删除它,这就是我需要的。
Vaibhav Vishal

24

这是一种使用ES6扩展语法从状态中删除数组中元素的方法。

onRemovePerson: (index) => {
  const data = this.state.data;
  this.setState({ 
    data: [...data.slice(0,index), ...data.slice(index+1)]
  });
}

1
谢谢!这感觉是最自然的方式。
fabiomaia

3

即使其他人遇到与我相同的问题,@ pscl已经正确回答了这个问题,但我还是想在这里提出建议。在这4种方法中,我选择将es6语法与箭头函数一起使用,因为它简洁明了且不依赖外部库:

将Array.prototype.filter与ES6箭头函数一起使用

removeItem(index) {
  this.setState((prevState) => ({
    data: prevState.data.filter((_, i) => i != index)
  }));
}

如您所见,我做了些微修改以忽略索引(!==to !=)的类型,因为在我的情况下,我是从字符串字段中检索索引的。

如果您在客户端删除元素时看到奇怪的行为,那么另一个有用的提示是,切勿使用数组的索引作为元素的键

// bad
{content.map((content, index) =>
  <p key={index}>{content.Content}</p>
)}

当React对虚拟DOM进行更改时,它将查看键以确定已更改的内容。因此,如果您使用的是索引,但数组中少了一个,它将删除最后一个。而是将内容的ID用作键,像这样。

// good
{content.map(content =>
  <p key={content.id}>{content.Content}</p>
)}

上面是相关帖子的答案摘录。

大家快乐编码!


1

如果要删除元素(不带索引),则可以使用此功能

removeItem(item) {
  this.setState(prevState => {
    data: prevState.data.filter(i => i !== item)
  });
}

1

正如上面对ephrion的答案的评论中提到的那样,filter()可能很慢,尤其是对于大型数组,因为它循环查找似乎已经确定的索引。这是一个干净但效率低下的解决方案。

另一种选择是,可以简单地“切出”所需的元素并连接片段。

var dummyArray = [];    
this.setState({data: dummyArray.concat(this.state.data.slice(0, index), this.state.data.slice(index))})

希望这可以帮助!


0

您可以使用单行辅助函数使代码更具可读性:

const removeElement = (arr, i) => [...arr.slice(0, i), ...arr.slice(i+1)];

然后像这样使用它:

this.setState(state => ({ places: removeElement(state.places, index) }));

0

只是一个建议,在您的代码中而不是使用let newData = prevState.data您可以使用ES6中引入的传播,您可以将其let newData = ...prevState.data 用于复制数组

三个点...表示扩展运算符剩余参数

它允许将数组表达式或字符串或任何可以迭代的内容扩展到期望零个或多个参数用于函数调用或数组元素的地方。

另外,您可以使用以下命令从数组中删除项目

onRemovePerson: function(index) {
  this.setState((prevState) => ({
    data: [...prevState.data.slice(0,index), ...prevState.data.slice(index+1)]
  }))
}

希望这有助于!!


-3

这是一种简单的方法:

removeFunction(key){
  const data = {...this.state.data}; //Duplicate state.
  delete data[key];                  //remove Item form stateCopy.
  this.setState({data});             //Set state as the modify one.
}

希望能帮助到你!!!


介意解释吗?
Tiberiu Maxim

5
我认为这是因为delete删除项目但索引不会更新,因此对于常规需求而言这不是一个好方法。
Kunok
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.