Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue 2 - Mutating props vue-warn

People also ask

Can you mutate a prop Vue?

Mutating props in Vue is an anti-pattern Yes, in Vue, mutating props like this is considered to be an anti-pattern. Meaning — please don't do it, or you'll cause a lot of headaches for yourself. Why an anti-pattern? In Vue, we pass data down the the component tree using props.

How do you not mutate props in Vue?

The answer is simple, you should break the direct prop mutation by assigning the value to some local component variables(could be data property, computed with getters, setters, or watchers). Here's a simple solution using the watcher. It's what I use to create any data input components and it works just fine.

Are Vue props reactive?

Props and data are both reactiveWith Vue you don't need to think all that much about when the component will update itself and render new changes to the screen. This is because Vue is reactive. Instead of calling setState every time you want to change something, you just change the thing!

How do you pass all Vue props to a child?

Vue actually makes this very easy for us. All component props are available in the $props object ( this. $props if you're not in the template), and we can use the Vue directive v-bind without specifying a specific prop name in order to bind a whole object of props to the child.


This has to do with the fact that mutating a prop locally is considered an anti-pattern in Vue 2

What you should do now, in case you want to mutate a prop locally, is to declare a field in your data that uses the props value as its initial value and then mutate the copy:

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    data: function () {
        return {
            mutableList: JSON.parse(this.list);
        }
    }
});

You can read more about this on Vue.js official guide


Note 1: Please note that you should not use the same name for your prop and data, i.e.:

data: function () { return { list: JSON.parse(this.list) } } // WRONG!!

Note 2: Since I feel there is some confusion regarding props and reactivity, I suggest you to have a look on this thread


The Vue pattern is props down and events up. It sounds simple, but is easy to forget when writing a custom component.

As of Vue 2.2.0 you can use v-model (with computed properties). I have found this combination creates a simple, clean, and consistent interface between components:

  • Any props passed to your component remains reactive (i.e., it's not cloned nor does it require a watch function to update a local copy when changes are detected).
  • Changes are automatically emitted to the parent.
  • Can be used with multiple levels of components.

A computed property permits the setter and getter to be separately defined. This allows the Task component to be rewritten as follows:

Vue.component('Task', {
    template: '#task-template',
    props: ['list'],
    model: {
        prop: 'list',
        event: 'listchange'
    },
    computed: {
        listLocal: {
            get: function() {
                return this.list
            },
            set: function(value) {
                this.$emit('listchange', value)
            }
        }
    }
})  

The model property defines which prop is associated with v-model, and which event will be emitted on changes. You can then call this component from the parent as follows:

<Task v-model="parentList"></Task>

The listLocal computed property provides a simple getter and setter interface within the component (think of it like being a private variable). Within #task-template you can render listLocal and it will remain reactive (i.e., if parentList changes it will update the Task component). You can also mutate listLocal by calling the setter (e.g., this.listLocal = newList) and it will emit the change to the parent.

What's great about this pattern is that you can pass listLocal to a child component of Task (using v-model), and changes from the child component will propagate to the top level component.

For example, say we have a separate EditTask component for doing some type of modification to the task data. By using the same v-model and computed properties pattern we can pass listLocal to the component (using v-model):

<script type="text/x-template" id="task-template">
    <div>
        <EditTask v-model="listLocal"></EditTask>
    </div>
</script>

If EditTask emits a change it will appropriately call set() on listLocal and thereby propagate the event to the top level. Similarly, the EditTask component could also call other child components (such as form elements) using v-model.


Vue just warns you: you change the prop in the component, but when parent component re-renders, "list" will be overwritten and you lose all your changes. So it is dangerous to do so.

Use computed property instead like this:

Vue.component('task', {
    template: '#task-template',
    props: ['list'],
    computed: {
        listJson: function(){
            return JSON.parse(this.list);
        }
    }
});

If you're using Lodash, you can clone the prop before returning it. This pattern is helpful if you modify that prop on both the parent and child.

Let's say we have prop list on component grid.

In Parent Component

<grid :list.sync="list"></grid>

In Child Component

props: ['list'],
methods:{
    doSomethingOnClick(entry){
        let modifiedList = _.clone(this.list)
        modifiedList = _.uniq(modifiedList) // Removes duplicates
        this.$emit('update:list', modifiedList)
    }
}