Files
agent-skills/skills/vue-best-practices/reference/computed-array-mutation.md
Jason Woltje f5792c40be feat: Complete fleet — 94 skills across 10+ domains
Pulled ALL skills from 15 source repositories:
- anthropics/skills: 16 (docs, design, MCP, testing)
- obra/superpowers: 14 (TDD, debugging, agents, planning)
- coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth)
- better-auth/skills: 5 (auth patterns)
- vercel-labs/agent-skills: 5 (React, design, Vercel)
- antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo)
- Plus 13 individual skills from various repos

Mosaic Stack is not limited to coding — the Orchestrator and
subagents serve coding, business, design, marketing, writing,
logistics, analysis, and more.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 16:27:42 -06:00

149 lines
4.3 KiB
Markdown

---
title: Avoid Mutating Methods on Arrays in Computed Properties
impact: HIGH
impactDescription: Array mutating methods in computed modify source data causing unexpected behavior
type: capability
tags: [vue3, computed, arrays, mutation, sort, reverse]
---
# Avoid Mutating Methods on Arrays in Computed Properties
**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.
## Task Checklist
- [ ] Always create a copy of arrays before using mutating methods
- [ ] Use spread operator `[...array]` or `slice()` to copy arrays
- [ ] Prefer non-mutating alternatives when available
- [ ] Be aware which array methods mutate vs return new arrays
**Incorrect:**
```vue
<script setup>
import { ref, computed } from 'vue'
const items = ref([3, 1, 4, 1, 5, 9, 2, 6])
const users = ref([
{ name: 'Alice', age: 30 },
{ name: 'Bob', age: 25 }
])
// BAD: sort() mutates the original array!
const sortedItems = computed(() => {
return items.value.sort((a, b) => a - b)
})
// BAD: reverse() mutates the original array!
const reversedItems = computed(() => {
return items.value.reverse()
})
// BAD: Both arrays now point to the same mutated data
// items.value and sortedItems.value are the SAME array
// items.value and reversedItems.value are the SAME array
// BAD: Chained mutations
const sortedUsers = computed(() => {
return users.value.sort((a, b) => a.age - b.age)
})
</script>
<template>
<!-- Original array is corrupted! -->
<div>Original: {{ items }}</div>
<div>Sorted: {{ sortedItems }}</div>
</template>
```
**Correct:**
```vue
<script setup>
import { ref, computed } from 'vue'
const items = ref([3, 1, 4, 1, 5, 9, 2, 6])
const users = ref([
{ name: 'Alice', age: 30 },
{ name: 'Bob', age: 25 }
])
// GOOD: Spread operator creates a copy first
const sortedItems = computed(() => {
return [...items.value].sort((a, b) => a - b)
})
// GOOD: slice() also creates a copy
const reversedItems = computed(() => {
return items.value.slice().reverse()
})
// GOOD: Copy before sorting objects
const sortedUsers = computed(() => {
return [...users.value].sort((a, b) => a.age - b.age)
})
// GOOD: Use toSorted() (ES2023) - non-mutating
const sortedItemsModern = computed(() => {
return items.value.toSorted((a, b) => a - b)
})
// GOOD: Use toReversed() (ES2023) - non-mutating
const reversedItemsModern = computed(() => {
return items.value.toReversed()
})
</script>
<template>
<!-- Original array stays intact -->
<div>Original: {{ items }}</div>
<div>Sorted: {{ sortedItems }}</div>
<div>Reversed: {{ reversedItems }}</div>
</template>
```
## Mutating vs Non-Mutating Array Methods
| Mutating (Avoid in Computed) | Non-Mutating (Safe) |
|------------------------------|---------------------|
| `sort()` | `toSorted()` (ES2023) |
| `reverse()` | `toReversed()` (ES2023) |
| `splice()` | `toSpliced()` (ES2023) |
| `push()` | `concat()` |
| `pop()` | `slice(0, -1)` |
| `shift()` | `slice(1)` |
| `unshift()` | `[item, ...array]` |
| `fill()` | `map()` with new values |
## ES2023 Non-Mutating Alternatives
Modern JavaScript (ES2023) provides non-mutating versions of common array methods:
```javascript
// These return NEW arrays, safe for computed properties
const sorted = array.toSorted((a, b) => a - b)
const reversed = array.toReversed()
const spliced = array.toSpliced(1, 2, 'new')
const withReplaced = array.with(0, 'newFirst')
```
## Deep Copy for Nested Arrays
For arrays of objects where you might mutate nested properties:
```javascript
const items = ref([{ name: 'A', values: [1, 2, 3] }])
// Shallow copy - nested arrays still shared
const copied = computed(() => [...items.value])
// Deep copy if you need to mutate nested structures
const deepCopied = computed(() => {
return JSON.parse(JSON.stringify(items.value))
// Or use structuredClone():
// return structuredClone(items.value)
})
```
## Reference
- [Vue.js Computed Properties - Avoid Mutating Computed Value](https://vuejs.org/guide/essentials/computed.html#avoid-mutating-computed-value)
- [MDN Array Methods](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)