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);
}
},
}
}
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
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')
then it the parent do this on the child
<child-component v-on:myEvent="doSomething"></child-component>
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);
},
}
}
//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);
},
}
}
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
- 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. - 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.
- 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
Top comments (3)
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.
Ref: vuejs.org/v2/guide/state-managemen...
This is very interesting, i will implement this and see the possibilties, thank you for this
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...