Vue enforces a one-way data flow principle: data is passed from parent to child components through props, and changes should be communicated back to the parent by emitting events. Directly mutating a prop breaks this contract.
Directly reassigning a prop triggers a Vue readonly warning in development mode. Mutating a prop’s contents in place — for example, calling
push() on a prop array — is more insidious: Vue cannot detect it, yet the parent component’s state is silently changed because both
parent and child share the same reference. In either case, the modification makes the data flow hard to trace and leads to bugs that are difficult to
reproduce and debug.
To fix the issue, emit an event so the parent can update its own state, or create a local copy of the prop when a local modification is needed.
<script>
export default {
props: ['items'],
methods: {
add(item) {
this.items.push(item); // Noncompliant: mutating a prop reference
},
reset() {
this.items = []; // Noncompliant: reassigning a prop
}
}
}
</script>
<script>
export default {
props: ['items'],
methods: {
add(item) {
this.$emit('update:items', [...this.items, item]);
},
reset() {
this.$emit('update:items', []);
}
}
}
</script>
<script setup lang="ts">
const props = defineProps<{ items: string[] }>();
const emit = defineEmits<{ 'update:items': [value: string[]] }>();
function add(item: string) {
props.items.push(item); // Noncompliant: mutating a prop reference
}
function reset() {
props.items = []; // Noncompliant: reassigning a prop
}
</script>
<script setup lang="ts">
const props = defineProps<{ items: string[] }>();
const emit = defineEmits<{ 'update:items': [value: string[]] }>();
function add(item: string) {
emit('update:items', [...props.items, item]);
}
function reset() {
emit('update:items', []);
}
</script>