DEV Community

Cover image for Getting around prop immutability in Vue.js
Reynold Osei Adade
Reynold Osei Adade

Posted on

Getting around prop immutability in Vue.js

This article is for beginner and intermediate developers who have problems wrapping their head around why props are immutable and also why you cant just go ahead and edit it without actually having console errors.

The problem

Let say you have a component, in that component there is a method that calls an alert and if the alert is confirmed, then an index is found and then splice is done.

//ChildComponent.vue
export default{
 props: {
    skillsToMerge: {
      required: true,
      type: Array,
      default: () => []
    }
  },
 methods: {
  onRemoveSkill(skill) {
    const confirmRemoval = window.confirm(`Proceed to remove ${skill.name}?`);
    if (confirmRemoval) {
      const index = this.skillsToMerge.findIndex((s) => s.id === skill.id);
      this.skillsToMerge.splice(index, 1);
      }
    },
  }
}
Enter fullscreen mode Exit fullscreen mode

In any other situation, this would have been a perfectly fine activity, but when it comes to vue.js props this will throw a warning in the console, and if you are using ESlint it will highlight that line as an error

linting error

Why is this a problem?

Immutability for props means it never changes or should not be changed
Props originate from a single data source, that is why it is by default reactive which means that every action taken on the original data source will affect every other child using that data source or prop, now imagine that your data could be changed midway, it wouldn't make for a very reliable data reference. If every component that used the data was able to edit it error fixing would be a tedious task.

Solution

Think of props are that data you can copy and read but cant edit, or an article on dev.to that someone has written, you could read it, and copy and even share it but you cant edit the post, only the author can. Therefore props should only be edited in the pages or components where they originate from. In other words you can only edit it when it is a data variable and not a prop

This gives us 2 ways of going of editing our props without effectively breaking its immutability

Solution 1: Emitters

Emitters are a way for child components to trigger an action in the parent, they are created by using

this.$emit('myEvent')
Enter fullscreen mode Exit fullscreen mode

then it the parent do this on the child

<child-component v-on:myEvent="doSomething"></child-component>
Enter fullscreen mode Exit fullscreen mode

This will ensure that you can trigger a function in the parent on the data variable without directly editing props

Solution 2: functions

Functions or methods are one of the most useful syntaxes in code, and when you realize that functions could be used as props it effectively opens up unlimited possibilities. Lets look at the code below

//ChildComponent.vue
export default{
 name: 'ChildComponent',
 props: {
    skillsToMerge: {
      required: true,
      type: Array,
      default: () => []
    },
   mergeSkills: {
      type: Function,
      default: () => {},
    },
  },
 methods: {
  onRemoveSkill(skill) {
    const confirmRemoval = window.confirm(`Proceed to remove ${skill.name}?`);
    if (confirmRemoval) {
        this.mergeSkills(skill);
    },
  }
}
Enter fullscreen mode Exit fullscreen mode
//ParentComponent.vue
export default {
name: 'ParentComponent',
data(){
 return {
 skillsToMerge: [],
 }
}
methods: {
 mergeSkills(skill) {
      const index = this.skillsToMerge.findIndex((s) => s.id === skill.id);
      this.skillsToMerge.splice(index, 1);
    },
}

}
Enter fullscreen mode Exit fullscreen mode

Lets explain what is happening in the code above, instead of trying to splice the skillsToMerge prop we create a function inside the parent component where the skillsToMerge variable was created and edit it there

The reason it is better to do it this way is that

  1. 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.
  2. It makes code refactoring easier since editing the logic of the mergeSkills function in the parent component affects every component it is used in.
  3. If there are a lot of nested child components, emitting events upwards will take much more code to do and it will be quite easy to have an error.
  4. Finally when using inject-provide using methods becomes so much easier as there isn't a need to pushed events upwards and also methods don't need the reactivity that props do, hence it will make your code cleaner

Conclusion

There are surely more ways to deal with the limitations imposed by props due to its immutability but that limitations is also its greatest benefit due to the security it provides as a single source of data.
This method logic is taken from how Vuex manipulates state using actions, keeping that in mind will make the benefits clearer.

I hope this has benefited you. Until next time, happy coding

Discussion (3)

Collapse
syylaurence profile image
Laurence Ivan Sy

Thank you, this is very helpful in modifying the props of the parent.

I would like to add, you can store the prop on local state and then you can modify the data on the local state.

props : {
     skillsToMerge : {
          required: true,
          type: Array,
          default: () => []
     }
},

data : function () {
     my_skillsToMerge : this.skillsToMerge,
},

methods : {
      alterSkills : function () {
           // modify this.my_skillsToMerge here   
      }
},
Enter fullscreen mode Exit fullscreen mode

Ref: vuejs.org/v2/guide/state-managemen...

Collapse
reynoldadade profile image
Reynold Osei Adade Author

This is very interesting, i will implement this and see the possibilties, thank you for this

Collapse
warhead5555 profile image
Warhead5555

Nice article. I found props immutability hard to understand when i first started developing in VueJs.

However, i have a note about using functions in props:
"By passing functions down as props, you are linking both parent child components together with two-way data binding. In the example above, the child now needs to know about the context of the function prop from its parent. Every time execute runs, it needs to check for and execute the callback function passed down."

I copied this text from: medium.com/js-dojo/passing-functio...