【问题标题】:Removing element from array in component state从组件状态的数组中删除元素
【发布时间】:2015-06-14 03:54:31
【问题描述】:

我正在尝试找到从处于组件状态的数组中删除元素的最佳方法。由于我不应该直接修改 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 配合得很好。 link
  • 我看不出你的代码有什么问题。事实上,这是一种非常惯用的方式。

标签: javascript reactjs


【解决方案1】:

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

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

【讨论】:

  • @HussienK _ 有时用于表示未使用的参数。在这里,它是数组中的当前项。
  • 太棒了。我一直在使用 .filter 但从未考虑在这种情况下使用它。 :D
  • 这是干净的代码,但是,对于大型数组,这种方法很慢。原因是,它遍历整个数组以找到看起来已经确定的索引。
  • @MattEllis 因为 React 的状态更新无论如何都是不可变的,所以无论如何你都会在列表的大小上产生 O(n) 来复制它。如果这对性能造成重大影响,我会感到惊讶。
  • 不推荐将this.state 评估为this.setState() 的输入,即使是不可变的。请参阅我上面的回答,以获取有关此主题的官方 React 文档的参考。
【解决方案2】:

您可以使用update() immutability helper from react-addons-update,它实际上在后台执行相同的操作,但您正在做的事情很好。

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

【讨论】:

  • 比你链接的那个简单得多:)
  • react-addons-update 现在已弃用(2016 年)。 immutability-helper 可作为替代品。 github.com/kolodny/immutability-helper 另外请参阅下面关于不直接在 this.setState() 中改变 this.state 的答案。
【解决方案3】:

我认为不鼓励在 setState() 中引用 this.state (State Updates May Be Asynchronous)。

文档建议使用带有回调函数的setState(),以便在更新发生时在运行时传入 prevState。这就是它的样子:

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

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

在 ES6 箭头函数中使用 Array.prototype.filter

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

使用 immutability-helper

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() 都会传递一个回调,不是对旧 this.state 的对象引用;

【讨论】:

  • 这是正确答案,另见The Power of Not Mutating Data
  • 使用 prevState 回调并添加了各种技术的示例。
  • 如果我这样做呢? this.setState({ data: [...this.state.data.slice(0, index), ...this.state.data.slice(index + 1)] }); 使用this.state 而不是@user1628461 显示的prevState 回调选项是错误的吗?
  • 这是最好的解决方案,虽然不是最简单的
  • 谢谢,使用 spread 也可以更新中间的元素而不是删除它,这就是我所需要的。
【解决方案4】:

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

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

【讨论】:

  • 谢谢!感觉这是最自然的方式。
【解决方案5】:

我想在这里插话,即使这个问题已经是answered correctly by @pscl,以防其他人遇到与我相同的问题。在 4 种方法中,我选择使用带有箭头函数的 es6 语法,因为它简洁且不依赖外部库:

在 ES6 箭头函数中使用 Array.prototype.filter

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

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

如果您在客户端删除元素时看到奇怪的行为,另一个有用的一点是永远不要使用数组的索引作为元素的键

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

以上内容摘自this answer from a related post

祝大家编码愉快!

【讨论】:

    【解决方案6】:

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

    作为替代方案,您可以简单地“切片”出所需的元素并将片段连接起来。

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

    希望这会有所帮助!

    【讨论】:

      【解决方案7】:

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

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

      【讨论】:

        【解决方案8】:

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

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

        然后像这样使用它:

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

        【讨论】:

          【解决方案9】:

          只是一个建议,在你的代码中而不是使用let newData = prevState.data,你可以使用ES6中引入的spread,你可以使用let newData = ...prevState.data 来复制数组

          三个点...代表Spread OperatorsRest Parameters

          它允许数组表达式或字符串或任何可以迭代的东西在需要零个或多个函数调用参数或数组元素的地方展开。

          此外,您可以使用以下方法从数组中删除项目

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

          希望这会有所帮助!

          【讨论】:

            【解决方案10】:

            这是一个简单的方法:

            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.
            }
            

            希望对你有帮助!!!

            【讨论】:

            • 想解释一下吗?
            • 我认为这是因为delete 删除了项目,但索引没有得到更新,因此这不是满足常规需求的好方法。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-06-01
            • 1970-01-01
            • 1970-01-01
            • 2020-04-28
            相关资源
            最近更新 更多