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

How to update state of nested array of array of objects without mutating

我正在尝试更新嵌套对象数组的状态,例如添加到结果[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
   });

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

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

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

如果您不这样做,该函数会将 {} 视为正文块。

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

这是工作版本:

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

因此,我们映射房间,然后 return 一个具有扩展语法的对象。使用 ...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 } );

评论后更新

我在这里使用 Object.assign 来替换项目而不改变原始数组。在使用 React 时(如果你更喜欢与 React 一起使用,这也适用于 Redux)我们应该避免改变我们的数据,即我们的状态。因此,当您计划更改状态中的某些内容时,您应该以正确的方式进行。您的问题实际上表明:"without mutating"。这就是我在这里使用 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 );

好的,解释到这里就够了:)

Thank you for the answer. I am a bit confused, am I supposed to set the state as follows: this.setState({ results: newResults });

是的。

also what if i want to change the state of all results array rooms results[0],results[1],results[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 并更改数据,return 新项目。所以最后,我们得到一个新数组,其中所有项目都发生了变化,但同样没有任何变化。

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