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>
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
---
|
||||
title: Avoid Comparing Reactive Objects with === Operator
|
||||
impact: HIGH
|
||||
impactDescription: Reactive proxies have different identity than original objects - comparison bugs are silent and hard to debug
|
||||
type: gotcha
|
||||
tags: [vue3, reactivity, proxy, comparison, debugging, identity]
|
||||
---
|
||||
|
||||
# Avoid Comparing Reactive Objects with === Operator
|
||||
|
||||
**Impact: HIGH** - Vue's `reactive()` returns a Proxy wrapper that has a different identity than the original object. Using `===` to compare reactive objects can lead to silent bugs where comparisons unexpectedly return `false`.
|
||||
|
||||
When you wrap an object with `reactive()`, the returned proxy is NOT equal to the original object. Additionally, accessing nested objects from a reactive object returns new proxy wrappers each time, which can cause identity comparison issues.
|
||||
|
||||
## Task Checklist
|
||||
|
||||
- [ ] Never compare reactive object instances with `===` directly
|
||||
- [ ] Use unique identifiers (ID, UUID) for object comparison instead
|
||||
- [ ] Use `toRaw()` on both sides when identity comparison is absolutely necessary
|
||||
- [ ] Consider using primitive identifiers from database records for comparison
|
||||
|
||||
**Incorrect:**
|
||||
```javascript
|
||||
import { reactive } from 'vue'
|
||||
|
||||
const original = { id: 1, name: 'Item' }
|
||||
const state = reactive(original)
|
||||
|
||||
// BUG: Always returns false - proxy !== original
|
||||
if (state === original) {
|
||||
console.log('Same object') // Never executes
|
||||
}
|
||||
|
||||
// BUG: Nested object comparison fails
|
||||
const items = reactive([{ id: 1 }, { id: 2 }])
|
||||
const item = items[0]
|
||||
|
||||
// Later...
|
||||
if (items[0] === item) {
|
||||
// May or may not work depending on Vue's proxy caching
|
||||
}
|
||||
|
||||
// BUG: Comparing items from different reactive sources
|
||||
const listA = reactive([{ id: 1 }])
|
||||
const listB = reactive([{ id: 1 }])
|
||||
if (listA[0] === listB[0]) {
|
||||
// Never true, even though they represent the same data
|
||||
}
|
||||
```
|
||||
|
||||
**Correct:**
|
||||
```javascript
|
||||
import { reactive, toRaw } from 'vue'
|
||||
|
||||
const original = { id: 1, name: 'Item' }
|
||||
const state = reactive(original)
|
||||
|
||||
// CORRECT: Use toRaw() for identity comparison
|
||||
if (toRaw(state) === original) {
|
||||
console.log('Same underlying object') // Works!
|
||||
}
|
||||
|
||||
// BEST: Use unique identifiers instead
|
||||
const items = reactive([
|
||||
{ id: 'uuid-1', name: 'Item 1' },
|
||||
{ id: 'uuid-2', name: 'Item 2' }
|
||||
])
|
||||
|
||||
function findItem(targetId) {
|
||||
return items.find(item => item.id === targetId)
|
||||
}
|
||||
|
||||
function isSelected(item) {
|
||||
return selectedId.value === item.id // Compare IDs, not objects
|
||||
}
|
||||
|
||||
// CORRECT: For Set/Map operations, use primitive keys
|
||||
const selectedIds = reactive(new Set())
|
||||
selectedIds.add(item.id) // Use ID, not object
|
||||
selectedIds.has(item.id) // Check by ID
|
||||
```
|
||||
|
||||
```javascript
|
||||
// When you must compare objects, use toRaw on both sides
|
||||
import { toRaw, isReactive } from 'vue'
|
||||
|
||||
function areEqual(a, b) {
|
||||
const rawA = isReactive(a) ? toRaw(a) : a
|
||||
const rawB = isReactive(b) ? toRaw(b) : b
|
||||
return rawA === rawB
|
||||
}
|
||||
```
|
||||
|
||||
## Reference
|
||||
- [Vue.js Reactivity in Depth](https://vuejs.org/guide/extras/reactivity-in-depth.html)
|
||||
- [Vue.js toRaw() API](https://vuejs.org/api/reactivity-advanced.html#toraw)
|
||||
Reference in New Issue
Block a user