在ReactJS中更新数组中的对象的最佳方法是什么?

109
如果您的状态中包含数组,并且该数组包含对象,那么如何轻松地使用更改一个对象来更新状态?
例如,从React教程修改:
var CommentBox = React.createClass({
  getInitialState: function() {
    return {data: [
      { id: 1, author: "john", text: "foo" },
      { id: 2, author: "bob", text: "bar" }
    ]};
  },
  handleCommentEdit: function(id, text) {
    var existingComment = this.state.data.filter({ function(c) { c.id == id; }).first();
    var updatedComments = ??; // not sure how to do this  

    this.setState({data: updatedComments});
  }
}

你能描述一下你想要做什么吗? - daniula
1
如果您想编辑评论,请查看以下链接-> https://github.com/tastejs/todomvc/blob/gh-pages/examples/react-backbone/js/todoItem.jsx。基本上,您列出的教程只是ToDo应用程序的简化版本。 - TYRONEMICHAEL
@daniula 我想要改变其中一条评论的文字。我会在上面添加更多细节。 - waterlooalex
可能是ReactJS中状态数组的正确修改的重复问题。 - sleske
4个回答

157

我相当喜欢使用Object.assign来完成这个任务,而不是使用不可变性辅助函数。

handleCommentEdit: function(id, text) {
    this.setState({
      data: this.state.data.map(el => (el.id === id ? Object.assign({}, el, { text }) : el))
    });
}

我认为这比使用splice更加简洁,而且不需要知道索引或显式处理未找到的情况。

如果您想要完全使用ES2018,也可以使用扩展语法而不是Object.assign来实现这一点。

this.setState({
  data: this.state.data.map(el => (el.id === id ? {...el, text} : el))
});

4
非常好的回答,谢谢!由于在setState中访问状态是不安全的,因此以下是更安全的代码版本:this.setState(prevState => ({ data: prevState.data.map(el => (el.id === id ? { ...el, text } : el)) }))。该代码会根据先前的状态更新数据,以确保不会访问过时或不稳定的状态。 - nevermind777
1
@Notloved为什么你提到的两种方式只是({},el,{text})或{...el,text},而不是({},el,{text:text})或{...el,text:text}?它如何知道它必须更新“text”键的值而不是其他键?如果你回答中的“text”单词是指键,那么我们在哪里传递值“text”?我想我缺乏一些基本的理解,不知道它是如何工作的。 - user3884753
2
@user3884753 {text}{text: text} 的简写。由于 text 被定义为变量,因此您可以在对象中将其用作名称和值,而无需定义两次。这是 ES6 中的新特性,有时被称为“简写属性名 - Nick Parsons
这个答案可以通过解释代码中正在发生的事情来改进。 - ViktorMS

55

在更新状态时,关键部分是将其视为不可变的。如果您可以保证它,任何解决方案都可以正常工作。

这是我使用immutability-helper的解决方案:

jsFiddle

  var update = require('immutability-helper');

  handleCommentEdit: function(id, text) {
    var data = this.state.data;
    var commentIndex = data.findIndex(function(c) { 
        return c.id == id; 
    });

    var updatedComment = update(data[commentIndex], {text: {$set: text}}); 
    
    var newData = update(data, {
        $splice: [[commentIndex, 1, updatedComment]]
    });
    this.setState({data: newData});
  },

以下关于状态数组的问题也可能有所帮助:


28

我希望更好地解释如何做和正在发生的事情。

  • 首先,在状态数组中找到要替换的元素的索引。
  • 其次,更新该索引处的元素。
  • 第三步,使用新集合调用setState
import update from 'immutability-helper';

// this.state = { employees: [{id: 1, name: 'Obama'}, {id: 2, name: 'Trump'}] } 

updateEmployee(employee) {
    const index = this.state.employees.findIndex((emp) => emp.id === employee.id);
    const updatedEmployees = update(this.state.employees, {$splice: [[index, 1, employee]]});  // array.splice(start, deleteCount, item1)
    this.setState({employees: updatedEmployees});
}

编辑:有一种更好的方法可以在不使用第三方库的情况下完成这个操作

const index = this.state.employees.findIndex(emp => emp.id === employee.id);
employees = [...this.state.employees]; // important to create a copy, otherwise you'll modify state outside of setState call
employees[index] = employee;
this.setState({employees});

15

有多种方法可以实现这个,我将向您展示我通常使用的方法。在使用 React 中的数组时,通常会传递一个带有当前索引值的自定义属性。在下面的示例中,我传递了 data-index 属性,data- 是 HTML5 的约定。

例如:

//handleChange method.
handleChange(e){
  const {name, value} = e,
        index = e.target.getAttribute('data-index'), //custom attribute value
        updatedObj = Object.assign({}, this.state.arr[i],{[name]: value});
      
  //update state value.
  this.setState({
    arr: [
      ...this.state.arr.slice(0, index),
      updatedObj,
      ...this.state.arr.slice(index + 1)
    ]
  })
  }


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