iamrobin.
iamrobin.

Reputation: 1634

Prevent prop from overwriting the data

I'm new to vue.js and struggling with the following scenario. I send an array filled with objects via props to my router-view.

Inside one of my router-view components I use this array in multiple functions, reference it with 'this.data' and safe it inside the functions in a new variable so I don't overwrite the actual prop data.

However the functions overwrite the original prop data and manipulate the data of the prop.

Here is an abstract example of my question:

App.vue

<template>
  <div>
    <router-view :data='data'></router-view>
  </div>
</template>

<script>
export default {
  data: function() {
    return {
      data: [],
    };
  },

  created: function() {
    this.getData();
  },

  methods: {
    getData: function() {
      this.data = // array of objects
    },
  }

route component:

<script>
export default {

  props: {
    data: Array,
  },

  data: function() {
    return {
      newData1 = [],
      newData2 = [],
    }
  }

  created: function() {
    this.useData1();
    this.useData2();
  },

  methods: {
    useData1: function() {
      let localData = this.data;
      // do something with 'localData'
      this.newData1 = localData;
    }

    useData2: function() {
      let localData = this.data;
      // do something with 'localData'
      this.newData2 = localData;
    }
  }
}
</script>

The 'localData' in useData2 is manipulated from changes in useData1, whereby I don't overwrite the data prop.

Why do I overwrite the prop and how can i prevent it?

Upvotes: 1

Views: 471

Answers (3)

Vladislav Ladicky
Vladislav Ladicky

Reputation: 2487

I think this is most readable, "declarative" way:

First, install lodash npm i lodash. Then import desired function, not the whole library, and initialize your data with array from props.

<script>
  import cloneDeep from 'lodash/cloneDeep'

  export default {
    props: {
      data: Array
    },

    data () {
      return {
        // initialize once / non reactive
        newData1: cloneDeep(this.data),
        newData2: cloneDeep(this.data)
      }
    }
  }
</script>

Upvotes: 0

Roland
Roland

Reputation: 27819

@Arman Charan is right on his answer. Object and arrays are not primitive types but reference.

There is an awesome video explanation here => JavaScript - Reference vs Primitive Values/ Types

So for reference types you first have to clone it on another variable and later modify this variable without the changes affecting the original data.

However for nested arrays and objects in high level the spead and Array.from will not work.

If you are using Lodash you can use _.cloneDeep() to clone an array or an object safely.

I like functional programming and I use Lodash which I strongly recommend.

So you can do:

let original_reference_type = [{ id:1 }, { id: 2 }]
let clone_original = _.cloneDeep(original_reference_type)

clone_original[0].id = "updated"
console.log(original_reference_type) //[{ id:1 }, { id: 2 }] => will not change
console.log(clone_original) // [{ id: "updated" }, { id: 2 }]

Suggestion: For simple arrays and objects use:

Objects:

let clone_original_data = {...original_data} or

let clone_original_data = Object.assign({}, original_data)

Arrays:

let clone_original_data = [...original_data] or

let clonse_original_data = original_data.slice()

For complex and high nested arrays or Objects go with Lodash's _.cloneDeep()

Upvotes: 1

Arman Charan
Arman Charan

Reputation: 5797

The problem you're experiencing a side effect of copying this.data by reference, rather than value.

The solution is to use a technique commonly referred to as cloning. Arrays can typically be cloned using spread syntax or Array.from().

See below for a practical example.

// Methods.
methods: {

  // Use Data 1.
  useData1: function() {
    this.newData1 = [...this.data]
  },

  // Use Data 2.
  useData2: function() {
    this.newData2 = Array.from(this.data)
  }

}

Upvotes: 1

Related Questions