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>
4.0 KiB
4.0 KiB
title, impact, impactDescription, type, tags
| title | impact | impactDescription | type | tags | |||||
|---|---|---|---|---|---|---|---|---|---|
| Never Mutate Computed Property Return Values | HIGH | Mutating computed values causes silent failures and lost changes | capability |
|
Never Mutate Computed Property Return Values
Impact: HIGH - The returned value from a computed property is derived state - a temporary snapshot. Mutating this value leads to bugs that are difficult to debug.
Important: Mutations DO persist while the computed cache remains valid, but are lost when recomputation occurs. The danger lies in unpredictable cache invalidation timing - any change to the computed's dependencies triggers recomputation, silently discarding your mutations. This makes bugs intermittent and hard to reproduce.
Every time the source state changes, a new snapshot is created. Mutating a snapshot is meaningless because it will be discarded on the next recalculation.
Task Checklist
- Treat computed return values as read-only
- Update the source state instead of the computed value
- Use writable computed properties if bidirectional binding is needed
- Avoid array mutating methods (push, pop, splice, reverse, sort) on computed arrays
Incorrect:
<script setup>
import { ref, computed } from 'vue'
const books = ref(['Vue Guide', 'React Handbook'])
const publishedBooks = computed(() => {
return books.value.filter(book => book.includes('Guide'))
})
function addBook() {
// BAD: Mutating computed value - change will be lost!
publishedBooks.value.push('New Book')
}
// BAD: Mutating computed array
const sortedBooks = computed(() => books.value.filter(b => b))
function reverseBooks() {
// BAD: This mutates the computed snapshot
sortedBooks.value.reverse()
}
</script>
<script>
export default {
data() {
return {
author: {
name: 'John',
books: ['Book A', 'Book B']
}
}
},
computed: {
authorBooks() {
return this.author.books
}
},
methods: {
addBook() {
// BAD: Mutating computed value
this.authorBooks.push('New Book')
}
}
}
</script>
Correct:
<script setup>
import { ref, computed } from 'vue'
const books = ref(['Vue Guide', 'React Handbook'])
const publishedBooks = computed(() => {
return books.value.filter(book => book.includes('Guide'))
})
function addBook(bookName) {
// GOOD: Update the source state
books.value.push(bookName)
}
// GOOD: Create a copy before mutating for display
const sortedBooks = computed(() => {
return [...books.value].sort() // Spread to create copy before sort
})
const reversedBooks = computed(() => {
return [...books.value].reverse() // Spread to create copy before reverse
})
</script>
<script>
export default {
data() {
return {
author: {
name: 'John',
books: ['Book A', 'Book B']
}
}
},
computed: {
authorBooks() {
return this.author.books
}
},
methods: {
addBook(bookName) {
// GOOD: Update source state
this.author.books.push(bookName)
}
}
}
</script>
Writable Computed for Bidirectional Binding
If you genuinely need to "set" a computed value, use a writable computed property:
<script setup>
import { ref, computed } from 'vue'
const firstName = ref('John')
const lastName = ref('Doe')
// Writable computed with getter and setter
const fullName = computed({
get() {
return `${firstName.value} ${lastName.value}`
},
set(newValue) {
// Update source state based on the new value
const parts = newValue.split(' ')
firstName.value = parts[0] || ''
lastName.value = parts[1] || ''
}
})
// Now this is valid:
fullName.value = 'Jane Smith' // Updates firstName and lastName
</script>