Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Vue 3 debugging reference for reactivity issues, computed errors, watcher bugs, async failures, and SSR hydration problems.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
reference/computed-array-mutation.md
1---2title: Avoid Mutating Methods on Arrays in Computed Properties3impact: HIGH4impactDescription: Array mutating methods in computed modify source data causing unexpected behavior5type: capability6tags: [vue3, computed, arrays, mutation, sort, reverse]7---89# Avoid Mutating Methods on Arrays in Computed Properties1011**Impact: HIGH** - JavaScript array methods like `reverse()`, `sort()`, `splice()`, `push()`, `pop()`, `shift()`, and `unshift()` mutate the original array. Using them directly on reactive arrays inside computed properties will modify your source data, causing unexpected side effects and bugs.1213## Task Checklist1415- [ ] Always create a copy of arrays before using mutating methods16- [ ] Use spread operator `[...array]` or `slice()` to copy arrays17- [ ] Prefer non-mutating alternatives when available18- [ ] Be aware which array methods mutate vs return new arrays1920**Incorrect:**21```vue22<script setup>23import { ref, computed } from 'vue'2425const items = ref([3, 1, 4, 1, 5, 9, 2, 6])26const users = ref([27{ name: 'Alice', age: 30 },28{ name: 'Bob', age: 25 }29])3031// BAD: sort() mutates the original array!32const sortedItems = computed(() => {33return items.value.sort((a, b) => a - b)34})3536// BAD: reverse() mutates the original array!37const reversedItems = computed(() => {38return items.value.reverse()39})4041// BAD: Both arrays now point to the same mutated data42// items.value and sortedItems.value are the SAME array43// items.value and reversedItems.value are the SAME array4445// BAD: Chained mutations46const sortedUsers = computed(() => {47return users.value.sort((a, b) => a.age - b.age)48})49</script>5051<template>52<!-- Original array is corrupted! -->53<div>Original: {{ items }}</div>54<div>Sorted: {{ sortedItems }}</div>55</template>56```5758**Correct:**59```vue60<script setup>61import { ref, computed } from 'vue'6263const items = ref([3, 1, 4, 1, 5, 9, 2, 6])64const users = ref([65{ name: 'Alice', age: 30 },66{ name: 'Bob', age: 25 }67])6869// GOOD: Spread operator creates a copy first70const sortedItems = computed(() => {71return [...items.value].sort((a, b) => a - b)72})7374// GOOD: slice() also creates a copy75const reversedItems = computed(() => {76return items.value.slice().reverse()77})7879// GOOD: Copy before sorting objects80const sortedUsers = computed(() => {81return [...users.value].sort((a, b) => a.age - b.age)82})8384// GOOD: Use toSorted() (ES2023) - non-mutating85const sortedItemsModern = computed(() => {86return items.value.toSorted((a, b) => a - b)87})8889// GOOD: Use toReversed() (ES2023) - non-mutating90const reversedItemsModern = computed(() => {91return items.value.toReversed()92})93</script>9495<template>96<!-- Original array stays intact -->97<div>Original: {{ items }}</div>98<div>Sorted: {{ sortedItems }}</div>99<div>Reversed: {{ reversedItems }}</div>100</template>101```102103## Mutating vs Non-Mutating Array Methods104105| Mutating (Avoid in Computed) | Non-Mutating (Safe) |106|------------------------------|---------------------|107| `sort()` | `toSorted()` (ES2023) |108| `reverse()` | `toReversed()` (ES2023) |109| `splice()` | `toSpliced()` (ES2023) |110| `push()` | `concat()` |111| `pop()` | `slice(0, -1)` |112| `shift()` | `slice(1)` |113| `unshift()` | `[item, ...array]` |114| `fill()` | `map()` with new values |115116## ES2023 Non-Mutating Alternatives117118Modern JavaScript (ES2023) provides non-mutating versions of common array methods:119120```javascript121// These return NEW arrays, safe for computed properties122const sorted = array.toSorted((a, b) => a - b)123const reversed = array.toReversed()124const spliced = array.toSpliced(1, 2, 'new')125const withReplaced = array.with(0, 'newFirst')126```127128## Deep Copy for Nested Arrays129130For arrays of objects where you might mutate nested properties:131132```javascript133const items = ref([{ name: 'A', values: [1, 2, 3] }])134135// Shallow copy - nested arrays still shared136const copied = computed(() => [...items.value])137138// Deep copy if you need to mutate nested structures139const deepCopied = computed(() => {140return JSON.parse(JSON.stringify(items.value))141// Or use structuredClone():142// return structuredClone(items.value)143})144```145146## Reference147- [Vue.js Computed Properties - Avoid Mutating Computed Value](https://vuejs.org/guide/essentials/computed.html#avoid-mutating-computed-value)148- [MDN Array Methods](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)149