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.7 KiB
4.7 KiB
title, impact, impactDescription, type, tags
| title | impact | impactDescription | type | tags | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| Choose watch vs watchEffect Based on Dependency Control Needs | MEDIUM | Wrong choice leads to unnecessary re-runs or missed dependency tracking | efficiency |
|
Choose watch vs watchEffect Based on Dependency Control Needs
Impact: MEDIUM - Using watch when watchEffect would be cleaner leads to repetitive code. Using watchEffect when watch is needed can cause unexpected re-runs or missed dependencies (especially with async).
Use watchEffect for simple cases where the callback uses the same state as what should trigger it. Use watch when you need precise control over what triggers the callback, access to old values, or lazy execution.
Task Checklist
- Use
watchEffectwhen callback logic uses the same state it should react to - Use
watchwhen you need old value comparison - Use
watchwhen you need lazy execution (not immediate) - Use
watchfor async callbacks with dependencies after await - Use
watchwhen callback should not run on initial mount
Comparison Table
| Feature | watch |
watchEffect |
|---|---|---|
| Dependency tracking | Explicit (you specify) | Automatic (uses accessed properties) |
| Lazy by default | Yes (runs only on change) | No (runs immediately) |
| Access old value | Yes | No |
| Async dependency tracking | Full control | Only before first await |
| Multiple sources | Array syntax | Automatic |
When to prefer watchEffect:
<script setup>
import { ref, watchEffect } from 'vue'
const todoId = ref(1)
const data = ref(null)
// GOOD: watchEffect is cleaner when callback uses same state
watchEffect(async () => {
const response = await fetch(
`https://api.example.com/todos/${todoId.value}`
)
data.value = await response.json()
})
</script>
When to prefer watch:
<script setup>
import { ref, watch } from 'vue'
const todoId = ref(1)
const data = ref(null)
// BETTER with watch when:
// 1. You need old value
watch(todoId, (newId, oldId) => {
console.log(`Changed from ${oldId} to ${newId}`)
})
// 2. You don't want immediate execution
watch(todoId, () => {
// Only runs when todoId changes, not on mount
fetchData()
})
// 3. You have dependencies after await
watch(todoId, async (id) => {
const response = await fetch(`/api/todos/${id}`)
// More reactive access here still triggers correctly
// because we explicitly specified todoId as the source
})
</script>
Avoid Redundant Code with watchEffect
<script setup>
import { ref, watch, watchEffect } from 'vue'
const searchQuery = ref('')
const category = ref('all')
const results = ref([])
// BAD: Repetitive - listing same deps in source and using in callback
watch(
[searchQuery, category],
([query, cat]) => {
fetchResults(query, cat) // Same variables repeated
}
)
// GOOD: watchEffect removes repetition
watchEffect(() => {
fetchResults(searchQuery.value, category.value)
})
</script>
Use watch for Lazy Behavior
<script setup>
import { ref, watch, watchEffect } from 'vue'
const userId = ref(null)
// BAD: Runs immediately even when userId is null
watchEffect(() => {
if (userId.value) {
loadUserProfile(userId.value)
}
})
// GOOD: Only runs when userId actually changes
watch(userId, (id) => {
if (id) {
loadUserProfile(id)
}
})
// ALSO GOOD: watch with immediate when you need both behaviors
watch(
userId,
(id) => {
if (id) loadUserProfile(id)
},
{ immediate: true } // Explicit about running immediately
)
</script>
Use watch for Old Value Comparison
<script setup>
import { ref, watch } from 'vue'
const status = ref('pending')
// Only watch() provides old value
watch(status, (newStatus, oldStatus) => {
if (oldStatus === 'pending' && newStatus === 'approved') {
showApprovalNotification()
}
if (oldStatus === 'approved' && newStatus === 'rejected') {
showRejectionWarning()
}
})
</script>
Use watch for Complex Async Dependencies
<script setup>
import { ref, watch } from 'vue'
const filters = ref({ status: 'active', sort: 'date' })
const page = ref(1)
const results = ref([])
// BETTER: watch with explicit sources for async
// All dependencies tracked regardless of await placement
watch(
[filters, page],
async ([currentFilters, currentPage]) => {
const data = await fetchWithFilters(currentFilters)
// These are still correctly tracked:
results.value = paginateResults(data, currentPage)
},
{ deep: true }
)
</script>