React:有效更新数组状态

时间:2015-01-18 13:54:43

标签: javascript reactjs

React的基本思想之一是状态更改应始终且仅通过this.setState(...)发生,而不是手动操作状态。但是对于一个状态是深度数组的情况,所以一个对象文字数组(实际上是一些JSON数据),更新该状态变得非常昂贵。如果我只想更新这样一个数组的一个元素,代码将类似于以下内容。

handleChange(index, newElement){
    var newStateArray = _.cloneDeep(this.state.myArray);
    newStateArray[index] = newElement;
    this.setState({myArray: newStateArray });
}

我知道标准解决方案是使用浅层复制,但这只是浅层阵列的副本。在我的情况下它不是,所以浅的复制在反应意义上是“错误的”,因为它会改变状态。那么一个人应该如何应对呢?我可以使用一个浅的副本,它可以工作,但它感觉很脏,在技术上是错误的。

编辑:或者澄清一下:如果我只是使用浅拷贝的东西并且确保告诉React手动更新,会发生什么不好吗?

编辑:哦,这似乎已经解决了。浅层复制不起作用,因为我认为它会。我是JS的新手,请原谅。供参考:

var a = [{a:1}, {b:2}, {c:3}]
var b = a.slice();
b[0] = 42; // I thought this would change a, but it doesn't!

2 个答案:

答案 0 :(得分:1)

如果您将所有对象视为不可变(即如果您需要更改属性,请先进行浅层复制),则可能会出错。例如:

开始状态:

var A = {
    foo: 3,
    bar: 7
};
var B = {
    baz: 11,
    boop: 5
};
var C = {
    myA: A,
    myB: B
};

假设我们想要改变C-> myB-> boop。 A中没有任何变化,因此我们不需要复制,但我们需要复制B和C:

var newB = {
    baz: B.baz,
    boop: 1000000 // we have to update this in B, so we need a new B
};
var newC = {
    myA: C.myA,
    myB: newB    // we have to update this in C, so we need a new C
};

(显然,您使用浅层复制制作每个部分的副本,然后在顶部分配更改;为了清晰起见,我只是手动写出副本)

当我们提交newC作为我们的新状态时,它将仅共享未更改的部分(在本例中为A),这很好,因为它们总是作为不可变对象处理。

概括,当您更改任何属性时,您需要制作一个包含该属性的对象的浅层副本,以及该对象的父级及其父级的父级等等。到你的根节点。你也可以用完全相同的方式考虑数组;为此,他们只是具有编号属性的对象。

如果您在任何地方都遵循这些规则进行更改,那么浅层数组副本就可以正常工作。这适用于你有状态历史的任何情况,而不仅仅是反应。

答案 1 :(得分:0)

请参阅React的Immutability Helpers

`CREATE TABLE Customer`
`(CustomerID INT(255) NOT NULL AUTO_INCREMENT,`
`FirstName VARCHAR(255) NOT NULL,`
`LastName VARCHAR(255) NOT NULL,`
`StreetAddress VARCHAR(255) NOT NULL,`
`Apartment VARCHAR(255) NOT NULL,`
`City VARCHAR(255) NOT NULL,`
`State VARCHAR(2) NOT NULL,`
`ZipCode CHAR(9) NOT NULL,`
`HomePhone CHAR(10) NOT NULL,`
`MobilePhone CHAR(10) NOT NULL,`
`OtherPhone CHAR(10) NOT NULL,`
`PRIMARY KEY (CustomerID));`
`INSERT INTO Customer `
`(FirstName, LastName, StreetAddress, Apartment, City, State,`
`ZipCode, HomePhone, MobilePhone, OtherPhone)`
`VALUES ('Max', 'Karl', '101 Main', ' ABC ', 'ABC', `
`'TX', 77077, 12345, 987654, 963258);`
`SELECT CustomerID, (CONCAT (firstname, ' ', Lastname)) as FullName,` `StreetAddress, Apartment, City, State, ZipCode, Homephone, `mobilephone,`otherphone FROM customer;`

`CREATE TABLE Donut`
`(DonutID INT(255) NOT NULL AUTO_INCREMENT,`
`Name VARCHAR(255) NOT NULL,`
`Description VARCHAR(255) NOT NULL,`
`UnitPrice DECIMAL(3,2) NOT NULL,`
`PRIMARY KEY (DonutID));`
`INSERT INTO Donut (Name, Description, UnitPrice)`
`VALUES ('Plain', 'Plain Donut', '1.50'),`
`('Glazed', 'Glazed Donut', '1.75'),`
`('Cinnamon', 'Cinnamon Donut', '1.75'),`
`('Chocolate', 'Chocolate Donut', '1.75'),`
`('Sprinkle', 'Sprinkle Donut', '1.75'),`
`('Gluten-Free', 'Gluten-Free Donut', '2.00');`
`CREATE INDEX DonutName ON Donut (Name);`

`CREATE TABLE DonutOrder`
`(DonutOrderID INT (255) NOT NULL AUTO_INCREMENT,`
`DonutID INT(255) NOT NULL,`
`Quantity INT(255),`
`PRIMARY KEY (DonutOrderID, donutID),`
`FOREIGN KEY (donutID) REFERENCES Donut(donutID),`
`INDEX Donut(donutID));`
`INSERT INTO DonutOrder (DonutID, Quantity)`
`VALUES ((SELECT DonutID FROM Donut WHERE DonutID = 1), 1),`
  ` ((SELECT DonutID FROM Donut WHERE DonutID = 2), 5),`
  ` ((SELECT DonutID FROM Donut WHERE DonutID = 3), 12),`
   `((SELECT DonutID FROM Donut WHERE DonutID = 4), 3),`
   `((SELECT DonutID FROM Donut WHERE DonutID = 5), 4),`
  ` ((SELECT DonutID FROM Donut WHERE DonutID =6), 5);`

`CREATE TABLE SalesInvoice`
`(CustomerID INT(255) NOT NULL,`
`DonutOrderID INT(255) NOT NULL AUTO_INCREMENT,`
`Date date NOT NULL,`
`Spec_Hnd_Inst VARCHAR(255),`
`PRIMARY KEY (DonutOrderID),`
`INDEX Customer (CustomerID),`
`FOREIGN KEY (CustomerID) REFERENCES Customer(CustomerID),`
`INDEX DonutOrder (donutOrderID));`

`SELECT Customer.CustomerID, FirstName, LastName, StreetAddress, Apartment, City, `
`State, ZipCode, HomePhone, MobilePhone, OtherPhone, Donut.DonutID, Name,`
`Description, UnitPrice, DonutOrder.DonutOrderID, DonutOrder.DonutID, `
`Quantity, SalesInvoice.DonutOrderID, SalesInvoice.CustomerID`
`FROM donut JOIN Customer ON SalesInvoice.CustomerID = Customer.CustomerID`
`JOIN DonutOrder ON Donut.DonutID = DonutOrder.DonutID`
`JOIN SalesInvoice ON DonutOrder.DonutID = SalesInvoice.DonutOrderID`