This question already has answers here:
Is this a good way to clone an object in ES6?
(10 answers)
What is the most efficient way to deep clone an object in JavaScript?
(67 answers)
Closed 2 years ago.
I am a newbie in React. I need some help to solve this issue
Code:
this.state={
testState: {
testArray: [
{ name: "bob" },
{ name: "alice" },
{ name: "john" }
]
}
}
testFn = () => {
let a;
a = { ...this.state.testState }; //using spread operator to copy the object instead of referencing
a.testArray.map((obj) => {
obj.name = "React is awesome"
})
console.log(this.state.testState)
}
Output:
testArray: Array(3)
0: {name: "React is awesome"}
1: {name: "React is awesome"}
2: {name: "React is awesome"}
I have to modify a without altering the state. But here, the state is also changed along with the iteration.
How can I solve this problem?
The spread operator only does a shallow copy
To guarantee a full object copy use
const copiedState = JSON.parse(JSON.stringify(yourState))
Better solution
Sove it at the source. Don't make deep state, its actually a key part of writing objects in the state. You should keep them very shallow. In this case you are already way deep with Obj -> Array -> Obj.
Related
This question already has answers here:
React doesn't rerender on an array state update
(2 answers)
React re-render not showing updated state array
(3 answers)
Correct modification of state arrays in React.js
(19 answers)
Right way to clone objects / arrays during setState in React
(7 answers)
Why is useState not triggering re-render?
(9 answers)
Closed last month.
I have an object employees that gets updated from user input through setEmployees() inside a function. I don't understand why for loop does not work but map() works to update the value on the webpage. I am not sure if this is react thing or useState() thing or maybe something else that I missed or did not know. Any idea? Thank you.
The employee object that gets updates from useState hook:
const [employees, setEmployees] = useState([
{
id: 0,
name: "Jason",
role: "Engineer",
img: 'https://images.pexels.com/photos/2598024/pexels-photo-2598024.jpeg'
},
{
id: 1,
name: "Abby",
role: "Manager",
img: 'https://images.pexels.com/photos/2598024/pexels-photo-2598024.jpeg'
}
]);
The case where I use for loop but it does not work:
function updateEmployee(id, newName, newRole) {
// Using for loop to update values
const updatedEmployees = employees
for (let i of updatedEmployees) {
if (i.id == id) {
i.name = newName
i.role = newRole
}
}
console.log(updatedEmployees) // updatedEmployees has been successfully updated
setEmployees(updatedEmployees) // object `employees` values have been updated but the update does not show on the webpage
}
The case where I use map() and it works:
function updateEmployee(id, newName, newRole) {
// Using map() to update values
const updatedEmployees = employees.map((employee) => {
if (id == employee.id) {
return {...employee, name: newName, role: newRole}
}
return employee
})
setEmployees(updatedEmployees) // object `employees` values have been updated and also update the values shown on the webpage
}
This question already has answers here:
Object copy using Spread operator actually shallow or deep?
(4 answers)
Closed 6 months ago.
I have a use case that I thought the Map object would be perfect for but there is confusing behaviour that appears to me as a bug, but Im after any information as to why this happens and possibly a solution to the behaviour.
For instance, say I have 2 objects:
const obj1 = { name: "John" };
const obj2 = { name: "Jane", age: 35 };
And I have defined and extra object for extra properties to add to both objects later:
const extProps = { gender: "unspecified", children: [] };
Create a new Map object and add the 2 objects:
const map = new Map();
map.set(obj1.name, obj1);
map.set(obj2.name, obj2);
Due to the objects being reference types I can assign the extra props like so:
Object.assign(obj1, { ...extProps });
Object.assign(obj2, { ...extProps });
Now I can get the values from the map using the keys like:
const johnObj = map.get("John");
const janeObj = map.get("Jane");
And the object have all the extra props like expected. The following statements update the individual objects in the map:
janeObj.gender = "female";
johnObj.age = 45;
Here is where the confusing behaviour I see is happening...
If I add an entry to the children array of either objects, it updates both
johnObj.children.push("jack");
obj2.children.push("jenny");
name: "John"
gender: "unspecified"
children: ["jack", "jenny"]
age: 45
name: "Jane"
age: 35
gender: "female"
children: ["jack", "jenny"]
What am I missing??
Like Konrad said in his comment, "arrays are also objects and are reference types".
The issue is that the spread operator (...) only goes on level deep, so for the array in extProps, is it not copied, it is the same one.
To solve this you can use a recursive function to "deep copy" an object.
Here is an example of a deep copy function:
const deepCopy = objIn => {
if (typeof objIn !== 'object' || objIn === null) return objIn;
let objOut = Array.isArray(objIn) ? [] : {};
for (const key in objIn) {
objOut[key] = deepCopy(objIn[key]);
}
return objOut;
}
This question already has answers here:
Add a property to a JavaScript object using a variable as the name? [duplicate]
(14 answers)
Closed 2 years ago.
I feel like my problem is really easy to solve, but I cannot see it. I have simple thing to do, get myObject from another function and store this in my storage object. For this task I created storageHandler function. Everything works fine, but Object.assign is not reaching my 'ID' that I declared in this function earlier. This is weird because I don't know how to tell my function that 'ID' is variable, not a string. I just expect it to be {1212313: {...}} but instead it gives me {ID: {...}}.
Someone have any idea how to fix it?
let storage = {}
const myObject = {
ID: '1223424525221',
name: 'Thomas',
mail: 'example#example.com'
}
storageHandler = data => {
const {ID} = data;
Object.assign(storage, {ID: data})
console.log(storage)
}
storageHandler(myObject)
That's because in javascript this
a = { b: 1 };
is the same as
a = { "b": 1 };
You should change the Object.assign() for something like this
storage[ID] = data;
You should use the value of ID as key of object using [].
Object.assign(storage, {[ID]: data})
You are using string as a property name. Use computed property name like [ID] instead of ID. Computed property allows you to have an expression be computed as a property name on an object.
let storage = {};
const myObject = {
ID: '1223424525221',
name: 'Thomas',
mail: 'example#example.com',
};
storageHandler = (data) => {
const { ID } = data;
Object.assign(storage, { [ID]: data });
console.log(storage);
};
storageHandler(myObject);
This question already has answers here:
How to deep merge instead of shallow merge?
(47 answers)
Closed 2 years ago.
I have a json object containing information about a person, and I want to update the data using jQuery extend function, the problem is that the child gets rewritten.
example:
var dadThen = {
name: "Adam",
age: 35,
child:{
name: "Ben",
age: 10
}
}
// dad now
var dadNow = {
age: 36,
child: {
age: 11
}
}
var newData = $.extend({}, dadThen, dadNow);
// The child name gets removed
// newData.child.name is undefined
How to fix this?
Isnt it like this
var newData = $.extend(true, {}, dadThen, dadNow);
first parameter is a flag whether to deep clone the object.
This question already has answers here:
What is the concept of Array.map?
(10 answers)
Closed 3 years ago.
I have a class component that uses the map method to access an array of objects. I Then use an implicit return to turn each object into a component. From my understanding the map method can only take an array, then pass a function to change the array.I don't understand why my code below works?
class App extends Component {
state = {
players: [
{
name: "Guil",
id: 1
},
{
name: "Treasure",
id: 2
},
{
name: "Ashley",
id: 3
},
{
name: "James",
id: 4
}
]
};
render() {
return (
<div className="scoreboard">
{this.state.players.map( player =>
<Player
name={player.name}
id={player.id}
key={player.id.toString()}
removePlayer={this.handleRemovePlayer}
/>
)}
</div>
);
}
}
From MDN
The map() method creates a new array populated with the results of calling a provided function on every element in the calling array.
The map() never change the original array but just create a new array according to the provided function.
In your code, the map() method creates a new array of Player components(the result of the provided callback).
The way you used is the same as what you said:"take an array, then pass a function to change the array".Here is the arrow function and an implicit return action.