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.