How to clone props object and make it non-reactive

Viewed 13629

I have some form data which I share with children components through props. Now I want to clone the prop object and make it non-reactive. In my case I want the user to be able to modify the props value without actually changing the cloned value. The cloned value should only be there to show the user what the form was when editing. Below code shows this:

    <template>
        <div>
            <div v-if="computedFormData">
                original prop title: {{orgData.title}}

                new title:
                <input type="text" v-model="formData.title"/> 
                //changing data here will also change orgData.title
            </div>

        </div>
    </template>

    <script>
        export default {
            props: ['formData'],
            data() {
                return {
                    orgData: [],
                }
            },
            computed: {
                computedFormData: function () {
                    this.orgData = this.formData;
                    return this.orgData;
                },
            },
            methods: {
            },
        }
    </script>

I have tried with Object.freeze(testData); but it doesnt work, both testData and orgData are reactive. Note also that using mounted or created property does not render orgData so I'm forced to use the computed property.

3 Answers

Try copying the prop values with Object.assign. No more issue with reactivity since the new, assigned values are just the copy instead of the reference to the source.

If your data object is a lot more complex, I'd recommend deepmerge in place of Object.assign.

Vue.component('FormData', {
  template: `
    <div>
      <div v-if="testData">
        <p>Original prop title: <strong>{{orgData.title}}</strong></p> 
        <p>Cloned prop title:</p>
        <input type="text" v-model="testData.title" />
      </div>
    </div>
  `,

  props: ['orgData'],

  data() {
    return {
      testData: Object.assign({}, this.orgData)
    }
  }
});

const vm = new Vue({
  el: '#app',

  data() {
    return {
      dummyForm: {
        title: 'Some title'
      }
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <form-data :org-data="dummyForm"></form-data>
</div>

Not entirely sure why but using Object.assign on a computed property did not work for me. I solved it by using a watch property for the props value:

    watch:{
        formData(){
            this.orgData = Object.assign({}, this.formData)
        }
    },

Object.assign is merely a shallow copy. If you have a copy consists that of only primitive data types (string, number, bigint, boolean, undefined, symbol, and null) it's ok. It to remove its reactivity. But, if you have a copy that has reference types you can’t shallow clone it to remove its reactivity.

For depping clone you can use the JSON.parse(JSON.stringify()) pattern. But keep in mind that is going to work if your data consists of supported JSON data types.

  props: ['orgData'],

  data() {
    return {
      cloneOrgData: JSON.parse(JSON.stringify(this.orgData))
    }
  }
Related