Computed properties in Vue are meant to be pure derivations of reactive state: given the same inputs, they should always return the same value without altering anything else. When a computed property performs a side effect, such as mutating component data, pushing to an array, or triggering an asynchronous operation, the behavior becomes unpredictable.
Vue’s reactivity system re-evaluates computed properties whenever their dependencies change. Side effects inside a computed property therefore run at unexpected times, potentially producing stale or inconsistent application state that is difficult to reason about.
To avoid side effects in a computed property, either derive the value from a copy of the data without mutating the original, or move the side
effect to a watch or watchEffect callback.
<script setup>
import { computed, ref } from 'vue';
const items = ref([{ price: 3 }, { price: 1 }, { price: 2 }]);
const sortedItems = computed(() => {
return items.value.sort((a, b) => a.price - b.price); // Noncompliant: mutating the reactive array in-place
});
</script>
<script setup>
import { computed, ref } from 'vue';
const items = ref([{ price: 3 }, { price: 1 }, { price: 2 }]);
const sortedItems = computed(() => {
return items.value.toSorted((a, b) => a.price - b.price);
});
</script>
<script>
export default {
data() {
return { items: [], discount: 0 };
},
computed: {
totalPrice() {
this.discount = this.items.length > 5 ? 10 : 0; // Noncompliant: mutating component data inside a computed property
return this.items.reduce((sum, item) => sum + item.price, 0);
}
}
}
</script>
<script>
export default {
data() {
return { items: [], discount: 0 };
},
computed: {
totalPrice() {
return this.items.reduce((sum, item) => sum + item.price, 0);
}
},
watch: {
items(newItems) {
this.discount = newItems.length > 5 ? 10 : 0;
}
}
}
</script>