Andrej
Andrej

Reputation: 437

Vue.js unexpected data and method behaviour

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

Answers (4)

Mahamudul Hasan
Mahamudul Hasan

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

BPS Julien
BPS Julien

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

Arthur Borba
Arthur Borba

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

Michal Lev&#253;
Michal Lev&#253;

Reputation: 37953

console.log()

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), use console.log(JSON.parse(JSON.stringify(obj)))

Upvotes: 2

Related Questions