Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VueJS right way to edit prop without changing parent data

In my parent vue component I have a user object.

If I pass that user object to a child component as a prop:

<child :user="user"></child>

and in my child component I update user.name, it will get updated in the parent as well.

I want to edit the user object in child component without the changes being reflected in the user object that is in parent component.

Is there a better way to achieve this than cloning the object with: JSON.parse(JSON.stringify(obj))?

like image 379
user3743266 Avatar asked Jun 27 '17 22:06

user3743266


People also ask

How do I use props to pass data to child components in Vue?

The way it works is that you define your data on the parent component and give it a value, then you go to the child component that needs that data and pass the value to a prop attribute so the data becomes a property in the child component. You can use the root component (App.

How do you handle props in Vue?

To specify the type of prop you want to use in Vue, you will use an object instead of an array. You'll use the name of the property as the key of each property, and the type as the value. If the type of the data passed does not match the prop type, Vue sends an alert (in development mode) in the console with a warning.

Why are props immutable in Vue?

It protects your prop by allowing only that function to edit your data outside the parent component. This is quite similar to having private variables. It makes code refactoring easier since editing the logic of the mergeSkills function in the parent component affects every component it is used in.


2 Answers

You don't have to use the JSON object.

const child = {
  props:["user"],
  data(){
    return {
      localUser: Object.assign({}, this.user)
    }
  }
}

Use localUser (or whatever you want to call it) inside your child.

Edit

I had modified a fiddle created for another answer to this question to demonstrate the above concept and @user3743266 asked

I'm coming to grips with this myself, and I'm finding this very useful. Your example works well. In the child, you've created an element in data that takes a copy of the prop, and the child works with the copy. Interesting and useful, but... it's not clear to me when the local copy gets updated if something else modifies the parent. I modified your fiddle, removing the v-ifs so everything is visible, and duplicating the edit component. If you modify name in one component, the other is orphaned and gets no changes?

The current component looks like this:

Vue.component('edit-user', {
  template: `
  <div>
    <input type="text" v-model="localUser.name">
    <button @click="$emit('save', localUser)">Save</button>
    <button @click="$emit('cancel')">Cancel</button>
  </div>
  `,
  props: ['user'],
  data() {
    return {
      localUser: Object.assign({}, this.user)
    }
  }
})

Because I made the design decision to use a local copy of the user, @user3743266 is correct, the component is not automatically updated. The property user is updated, but localUser is not. In this case, if you wanted to automatically update local data whenever the property changed, you would need a watcher.

Vue.component('edit-user', {
  template: `
  <div>
    <input type="text" v-model="localUser.name">
    <button @click="$emit('save', localUser)">Save</button>
    <button @click="$emit('cancel')">Cancel</button>
  </div>
  `,
  props: ['user'],
  data() {
    return {
      localUser: Object.assign({}, this.user)
    }
  },
  watch:{
    user(newUser){
        this.localUser = Object.assign({}, newUser)
    }
  }
})

Here is the updated fiddle.

This allows you full control over when/if the local data is updated or emitted. For example, you might want to check a condition before updating the local state.

  watch:{
    user(newUser){
      if (condition)
        this.localUser = Object.assign({}, newUser)
    }
  }

As I said elsewhere, there are times when you might want to take advantage of objects properties being mutable, but there are also times like this where you might want more control.

like image 146
Bert Avatar answered Oct 09 '22 14:10

Bert


in the above solutions, the watcher won't trigger at first binding, only at prop change. To solve this, use immediate=true

watch: {
  test: {
    immediate: true,
    handler(newVal, oldVal) {
      console.log(newVal, oldVal)
    },
  },
}
like image 41
Botea Florin Avatar answered Oct 09 '22 15:10

Botea Florin