简体   繁体   中英

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

I am tryin to update the state of nested array of objects, for instance add to results[0].rooms[2] -> loop rooms -> and add to each object 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 } 
 ]

my attempt to change the state without mutating

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

any help? what am i doin wrong

Your resultsRooms.map is wrong. First of all, it does not return an object. If you use an arrow function and want to return an object, you must enclose it with parentheses.

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

If you don't do that the function sees {} as a body block.

Your second problem is, map returns an array, does not do operations on items. So you can't do this: room.sunday.flag = true;

Here is the working version:

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

So, we map the rooms, then return an object with spread syntax. With ...room we keep the parts of the room other than the sunday object. With sunday: {...room["sunday"], flag: true } we keep the parts of the sunday other than the flag property. Also, actually we don't need to make a copy with:

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

Since we don't mutate it, with map we are creating a new array. So, here is the last version:

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

This part is OK, we don't mutate the state but if you use forEach on results you mutate the original data. Don't do it.

Here is a concise and maybe a cleaner alternative without using 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 } );

Update after comments

I used Object.assign here to replace the item without mutating the original array. When using React (also if you prefer to use with React, this applies to Redux also) we should avoid mutating our data, namely our state. So, when you plan to change something in the state you should do it in proper ways. Your question actually indicates that: "without mutating". This is why I used Object.assign here.

I could choose other methods like:

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

or

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

Those are ok just for replacing the item as a whole . What do I mean here? slice and spread syntax does not create deep copies, they just do shallow copies. If you change a property of an object in the newly created array, the original one also mutates. This also applies to objects when we change nested properties. Actually, original source is objects here.

Here is an example array:

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

and an example item to change for index 2 (last item here);

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

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

This is ok since we change a whole object here. Let's see:

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

Original one does not change. But if we do this:

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

Let's see:

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

So, our original array also changed. Mutation! If you don't change any property like this you can choose one of all three alternatives. But if you change a property you should think something better.

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

Duh! :) Maybe there are better ways of doing this :) In any case, be very careful about mutating the state.

Just with an object:

 const obj = { user: { id: 1, name: "foo", } }; const newObj = { ...obj } newObj.user.id = 1000 console.log( "new object", newObj ); console.log( "original object", obj ); 

Ok, this is enough for an explanation :)

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

Yes.

also what if i want to change the state of all results array rooms results[0],results[1],results[2] .....

Come on, you have a great tool here :) 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;
})

First, we map the results , for each item, we map rooms and change the data, return the new item. So in the end, we get a new array where all its items changed, but again without any mutation.

I suggest playing a little bit with map , filter and maybe reduce methods. Also looking for spread syntax or if you prefer Object.assign is a plus.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM