Reputation: 437
I have a Vue.js component with simple template
<div @click="createTargets(2)">
text
</div>
and script file is
export default {
name: 'test',
data() {
return {
targets: [],
};
},
methods: {
createTargets(targetCount) {
this.targets = [];
var emptyTarget = {
id: null,
};
for (var i = 0; i < targetCount; i++) {
var targetToPush = emptyTarget;
targetToPush.id = i;
console.log(targetToPush.id);
this.targets.push(targetToPush);
console.log(this.targets);
}
return {};
},
},
}
When I click text
, I get output
0
[{"id":1},{"id":1}]
1
[{"id":1},{"id":1}]
I cannot figure out why this is happening.
I would expect
0
[{"id":0}]
1
[{"id":0},{"id":1}]
Any ideas?
Upvotes: 1
Views: 194
Reputation: 2823
the main problem , you are updating the reference variable, so it i will keep always the latest data. just do like below hope problem will solve
createTargets(targetCount) {
this.targets = [];
for (var i = 0; i < targetCount; i++) {
var targetToPush = {id: null};
targetToPush.id = i;
console.log(targetToPush.id);
this.targets.push(targetToPush);
console.log(this.targets);
}
return {};
}
Upvotes: 0
Reputation: 126
The answer is quite simple really, an object is initialized only once, when it is assigned to a variable. If you assign this variable to a new variable, you are assigning the same object reference to a new variable. Updating Object1 will update Object2 and vice versa.
To circumvent this behavior, you can create a copy of the object when initializing Object2 using the new spread operator:
const targets = [];
const common = { commonProp: 'test' };
for (let i = 1; i <= count; i++) {
const target = { ...common, id: i };
targets.push(target);
}
this.targets = targets;
Note that you should avoid mutating your component's state in a loop. Even though the render loop is optimized and won't actually render count
times, it's still better to mutate your property only once as per example.
Also note that nested objects behave the same way. The solution above is called a shallow copy, in contrast, a deep copy will recursively crawl your object to copy sub objects/arrays.
const common = {
commonProp: { a: 1, b: 2 }
};
const object1 = { ...common, id: 1 };
const object2 = { ...common, id: 2 };
object1.commonProp.a = 2;
console.log(object1); // { commonProp: { a: 2, b: 2 } }
console.log(object2); // { commonProp: { a: 2, b: 2 } }
To avoid this issue, you can use a library to deep copy an object/array or create a class or factory function that will return a new object every time it is called.
// factory
const createTarget = id => ({
commonProp: { a: 1, b: 2 },
id,
});
// class
class Target {
constructor(id) {
this.id = id;
this.commonProp = { a: 1, b: 2 };
}
}
for (let i = 1; i <= count; i++) {
const target = createTarget(i); // or new Target(i);
targets.push(target);
}
I hope this explanation helped you understand this concept a bit better.
Good luck ;)
Upvotes: 2
Reputation: 473
You might print the array values (not the reference):
for (var i = 0; i < targetCount; i++) {
var targetToPush = emptyTarget;
targetToPush.id = i;
console.log(targetToPush.id);
this.targets.push(targetToPush);
// like this (es6 way):
console.log([...this.targets]);
// or like this (old way):
console.log(this.targets.slice());
}
When you console.log(this.targets)
you are printing directly the variable reference, and it's values will be chaging in the next loop, so it's also going to be changed in the browser console.
When you console.log([...this.targets])
you are printing the array values (not the reference) of that given loop interaction and they will stay the same, even if the array changes later.
Upvotes: 0
Reputation: 37953
Please be warned that if you log objects in the latest versions of Chrome and Firefox what you get logged on the console is a reference to the object, which is not necessarily the 'value' of the object at the moment in time you call
console.log()
, but it is the value of the object at the moment you open the console
Don't use
console.log(obj)
, useconsole.log(JSON.parse(JSON.stringify(obj)))
Upvotes: 2