如何在不改变的情况下更新对象数组的嵌套数组的状态

Posted

技术标签:

【中文标题】如何在不改变的情况下更新对象数组的嵌套数组的状态【英文标题】:How to update state of nested array of array of objects without mutating 【发布时间】:2019-02-03 16:10:24 【问题描述】:

我正在尝试更新嵌套对象数组的状态,例如添加到 results[0].rooms[2] -> 循环房间 -> 并添加到每个对象 room.sunday.flag

array of results:
results:  [ 
   dates:
   weekstart: 2018-08-26T04:00:00.000Z,
    weekend: 2018-09-02T03:59:59.000Z,
    daysofweek: [Array] ,
 need: [ '103', '204', '12', '234', '34', '555', '44' ],
 backorder:
  [ '100', '102', '150', '403', '344', '12', '3434', '23', '44' ],
 _id: 5b7b139465c29145d8d69ac2,
 iscurrent: true,
 isnext: false,
 rooms: [ [Object], [Object], [Object], [Object] ],
 user: 5b61b782719613486cdda7ec,
 __v: 9 ,

  dates:
   weekstart: 2018-09-02T04:00:00.000Z,
    weekend: 2018-09-09T03:59:59.000Z,
    daysofweek: [Array] ,
 need: [ '12', '13', '55', '45' ],
 backorder: [ '10', '11', '112' ],
 _id: 5b83fdc500b6b6dc493e9bb8,
 iscurrent: false,
 isnext: true,     rooms: [ [Object], [Object], [Object], 
 [Object] ],
 user: 5b61b782719613486cdda7ec,
 __v: 9  
 ]

我尝试在不改变的情况下改变状态

   const resultsRooms = [ 
     ...this.state.results[0].rooms ];
        const rooms = resultsRooms.map((room, roomIndex) => 
   
        room.sunday.flag = true;
   );

   const resultsUpdaye = this.results.forEach((element, index) => 
      if (index === 0) 
            elsment.rooms = rooms;
         
   );

   this.setState(
            results: resultsUpdaye
   );

有什么帮助吗?我做错了什么

【问题讨论】:

【参考方案1】:

您的resultsRooms.map 错误。首先,它不返回对象。如果你使用箭头函数并想返回一个对象,你必须用括号括起来。

const foo = () => (  foo: "bar"  );

如果您不这样做,该函数会将 视为主体块。

您的第二个问题是,map 返回一个数组,不对项目进行操作。所以你不能这样做:room.sunday.flag = true;

这是工作版本:

const rooms = resultsRooms.map((room, roomIndex) => 
   (  ...room, sunday: ...room["sunday"], flag: true   )
);

所以,我们映射房间,然后返回一个带有扩展语法的对象。使用...room,我们保留room 的部分,而不是sunday 对象。对于sunday: ...room["sunday"], flag: true ,我们保留sunday 的部分,而不是flag 属性。另外,实际上我们不需要复制:

const resultsRooms = [ 
     ...this.state.results[0].rooms ];

因为我们不改变它,所以我们正在使用map 创建一个新数组。所以,这是最后一个版本:

const rooms = results[0].rooms.map((room, roomIndex) => 
   (  ...room, sunday: ...room["sunday"], flag: true   )
);

这部分没问题,我们不会改变状态,但如果你在results 上使用forEach,你会改变原始数据。不要这样做。

这是一个简洁且可能更简洁的替代方案,无需使用forEach

const newRooms = results[0].rooms.map( room =>
    (  ...room, sunday: ...room["sunday"], flag: true   )
  );

const newItem =  ...results[0], rooms: newRooms ;
const newResults = Object.assign([], results,  0: newItem  );

cmets 后更新

我在这里使用Object.assign 来替换项目而不改变原始数组。在使用 React 时(如果您更喜欢与 React 一起使用,这也适用于 Redux)我们应该避免改变我们的数据,即我们的状态。因此,当您计划更改状态中的某些内容时,您应该以适当的方式进行。您的问题实际上表明:“没有变异”。这就是我在这里使用Object.assign 的原因。

我可以选择其他方法,例如:

const newResults = results.slice();
newResults[ 0 ] = newItem;

const newResults = [ ...results ];
newResults[ 0 ] = newItem;

这些都可以只是为了替换整个项目。我在这里是什么意思? slicespread syntax 不会创建深拷贝,它们只会创建浅拷贝。如果您更改新创建的数组中对象的属性,则原始数组也会发生变异。当我们更改嵌套属性时,这也适用于对象。实际上,原始来源是这里的对象。

这是一个示例数组:

const arr = [
   id:1, name:"foo" ,
   id:2, name:"bar" ,
   id:3, name:"baz" ,
];

以及要为索引 2 更改的示例项目(此处为最后一项);

const newItem =  id: 100, name: "change" ;

const newArr = [ ...arr ];
arr[ 2 ] = newItem;

这没关系,因为我们在这里更改了整个对象。让我们看看:

const arr = [
   id:1, name:"foo" ,
   id:2, name:"bar" ,
   id:3, name:"baz" ,
];

const newItem =  id: 100, name: "change" ;

const newArr = [ ...arr ];
newArr[ 2 ] = newItem;

console.log("new array", newArr );
console.log( "original array", arr );

原版不变。但如果我们这样做:

newArr[ 2 ].id = 100; // ie, we only want to change the id, right?

让我们看看:

const arr = [
   id:1, name:"foo" ,
   id:2, name:"bar" ,
   id:3, name:"baz" ,
];

const newArr = [ ...arr ];
newArr[ 2 ].id = 100;

console.log("new array", newArr );
console.log( "original array", arr );

所以,我们原来的数组也发生了变化。突变!如果您不更改任何此类属性,则可以选择所有三个选项之一。但如果你改变了一个属性,你应该考虑得更好。

const arr = [
   id:1, name:"foo" ,
   id:2, name:"bar" ,
   id:3, name:"baz" ,
];

const newArr = Object.assign( [], arr,  2:  ...arr[ 2 ], id: 100   );


console.log("new array", newArr );
console.log( "original array", arr );

呃! :) 也许有更好的方法可以做到这一点:) 无论如何,在改变状态时要非常小心。

只是一个对象:

const obj =  
  user: 
    id: 1,
    name: "foo",
  
;
const newObj =  ...obj 
newObj.user.id = 1000

console.log( "new object", newObj );
console.log( "original object", obj );

好吧,解释就够了:)

感谢您的回答。我有点困惑,我应该设置 状态如下:this.setState( results: newResults );

是的。

如果我想更改所有结果数组房间的状态怎么办 结果[0],结果[1],结果[2] .....

来吧,你有一个很棒的工具 :) map

const newResults = results.map( item => 
  const newRooms = item.rooms.map( room =>
    (  ...room, sunday: ...room["sunday"], flag: true   )
  );
  const newItem =  ...item, rooms: newRooms ;

  return newItem;
)

首先,我们映射results,对于每个项目,我们映射rooms 并更改数据,返回新项目。所以最后,我们得到了一个新数组,其中所有项目都发生了变化,但又没有任何变化。

我建议使用mapfilterreduce 方法。还寻找spread syntax 或者如果您更喜欢Object.assign 是一个加号。

【讨论】:

感谢您的回答。我有点困惑,我是不是应该将状态设置如下: this.setState( results: newResults );你也可以解释一下 Object.assign,我为什么需要它 如果我想改变所有结果数组的状态怎么办 results[0],results[1],results[2] .....

以上是关于如何在不改变的情况下更新对象数组的嵌套数组的状态的主要内容,如果未能解决你的问题,请参考以下文章

如何在不删除旧状态的情况下设置对象的状态数组

如何在不改变原始数组的情况下对数组进行排序?

如何设置映射输入的嵌套 JSON 数组对象的状态

如何在不创建嵌套数组的情况下将多个变量添加到数组

JS:从嵌套数组中删除对象并返回父数组

PostgreSQL,如何在不创建重复项的情况下将数组更新为现有数组?