Implicit Parent-child Communication β
Checks if props and events are used for parent-child component communication, instead of this.$parent or mutating props. Β Β
π https://vuejs.org/style-guide/rules-use-with-caution.html#implicit-parent-child-communication
β Why it's good to follow this rule? β
- Predictability: Using props and events for parent-child communication makes the data flow predictable. Developers can easily trace the flow of data between components, leading to fewer bugs and easier debugging.
- Encapsulation: Components remain more encapsulated, with clear interfaces for data input (props) and output (events).
- Reusability: Components that rely on props and events are more reusable, as they're not tightly coupled to parent components.
- Debugging: It's easier to debug issues when you can clearly see how data is being passed and modified.
- Testing: Components are easier to test in isolation when they rely on props and events rather than direct parent access.
- Scalability: As your application grows, consistent use of props and events helps manage complexity.
- Prevents unexpected side effects: Avoiding prop mutation reduces the risk of unexpected changes to parent state.
π± Examples of code for which this rule will throw a warning β
WARNING
Implicit parent-child communication can lead to unmanageable code and unexpected behavior. The following examples demonstrate cases where implicit communication occurs, such as prop mutation or using $parent or getCurrentInstance.
Example 1: Prop Mutation β
<script setup>
defineProps({
todo: {
type: Object,
required: true
}
})
</script>
<template>
<input v-model="todo.text">
</template>
In this example, the v-model
directive directly mutates the todo
prop, which is passed down from a parent component. This implicit communication can make the flow of data hard to follow.
Example 2: Using getCurrentInstance β
<script setup>
import { getCurrentInstance } from 'vue'
const props = defineProps({
todo: {
type: Object,
required: true
}
})
const instance = getCurrentInstance()
function removeTodo() {
const parent = instance.parent
if (!parent)
return
parent.props.todos = parent.props.todos.filter((todo) => {
return todo.id !== props.todo.id
})
}
</script>
<template>
<span>
{{ todo.text }}
<button @click="removeTodo">Γ</button>
</span>
</template>
In this example, the getCurrentInstance
function is used to access the parent componentβs properties, leading to an implicit and tight coupling between the parent and child components.
π€© How to fix it? β
TIP
To avoid implicit parent-child communication, ensure that data flow is explicit and clear. Here are some strategies:
Fixing Prop Mutation
β
Instead of mutating props directly, emit an event to the parent component to handle the change:
<script setup>
defineProps({
todo: {
type: Object,
required: true
}
})
const emit = defineEmits(['updateTodo'])
function updateText(newText) {
emit('updateTodo', { ...todo, text: newText })
}
</script>
<template>
<input :value="todo.text" @input="updateText($event.target.value)">
</template>
Fixing getCurrentInstance
β
Instead of accessing the parent component via getCurrentInstance
, pass down a callback function as a prop to handle the logic in the parent:
<script setup>
defineProps({
todo: {
type: Object,
required: true
},
onRemove: {
type: Function,
required: true
}
})
function removeTodo() {
onRemove(todo)
}
</script>
<template>
<span>
{{ todo.text }}
<button @click="removeTodo">Γ</button>
</span>
</template>
By refactoring in this way, the component maintains clear and predictable behavior without relying on implicit communication, making the codebase easier to manage and debug.