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

如果有一个数组作为状态的一部分,并且该数组包含对象,那么通过更改其中一个对象来更新状态有什么简单的方法吗?

例如,从关于反应的教程中修改的:

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});
}
}
168894 次浏览

While updating state the key part is to treat it as if it is immutable. Any solution would work fine if you can guarantee it.

Here is my solution using 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});
},

Following questions about state arrays may also help:

You can do this with multiple way, I am going to show you that I mostly used. When I am working with arrays in react usually I pass a custom attribute with current index value, in the example below I have passed data-index attribute, data- is html 5 convention.

Ex:

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

I quite like doing this with Object.assign rather than the immutability helpers.

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

I just think this is much more succinct than splice and doesn't require knowing an index or explicitly handling the not found case.

If you are feeling all ES2018, you can also do this with spread instead of Object.assign

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

I'm trying to explain better how to do this AND what's going on.

  • First, find the index of the element you're replacing in the state array.
  • Second, update the element at that index
  • Third, call setState with the new collection
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});
}

Edit: there's a much better way to do this w/o a 3rd party library

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