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/reactivity-proxy-identity-hazard.md
1---2title: Avoid Comparing Reactive Objects with === Operator3impact: HIGH4impactDescription: Reactive proxies have different identity than original objects - comparison bugs are silent and hard to debug5type: gotcha6tags: [vue3, reactivity, proxy, comparison, debugging, identity]7---89# Avoid Comparing Reactive Objects with === Operator1011**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`.1213When 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.1415## Task Checklist1617- [ ] Never compare reactive object instances with `===` directly18- [ ] Use unique identifiers (ID, UUID) for object comparison instead19- [ ] Use `toRaw()` on both sides when identity comparison is absolutely necessary20- [ ] Consider using primitive identifiers from database records for comparison2122**Incorrect:**23```javascript24import { reactive } from 'vue'2526const original = { id: 1, name: 'Item' }27const state = reactive(original)2829// BUG: Always returns false - proxy !== original30if (state === original) {31console.log('Same object') // Never executes32}3334// BUG: Nested object comparison fails35const items = reactive([{ id: 1 }, { id: 2 }])36const item = items[0]3738// Later...39if (items[0] === item) {40// May or may not work depending on Vue's proxy caching41}4243// BUG: Comparing items from different reactive sources44const listA = reactive([{ id: 1 }])45const listB = reactive([{ id: 1 }])46if (listA[0] === listB[0]) {47// Never true, even though they represent the same data48}49```5051**Correct:**52```javascript53import { reactive, toRaw } from 'vue'5455const original = { id: 1, name: 'Item' }56const state = reactive(original)5758// CORRECT: Use toRaw() for identity comparison59if (toRaw(state) === original) {60console.log('Same underlying object') // Works!61}6263// BEST: Use unique identifiers instead64const items = reactive([65{ id: 'uuid-1', name: 'Item 1' },66{ id: 'uuid-2', name: 'Item 2' }67])6869function findItem(targetId) {70return items.find(item => item.id === targetId)71}7273function isSelected(item) {74return selectedId.value === item.id // Compare IDs, not objects75}7677// CORRECT: For Set/Map operations, use primitive keys78const selectedIds = reactive(new Set())79selectedIds.add(item.id) // Use ID, not object80selectedIds.has(item.id) // Check by ID81```8283```javascript84// When you must compare objects, use toRaw on both sides85import { toRaw, isReactive } from 'vue'8687function areEqual(a, b) {88const rawA = isReactive(a) ? toRaw(a) : a89const rawB = isReactive(b) ? toRaw(b) : b90return rawA === rawB91}92```9394## Reference95- [Vue.js Reactivity in Depth](https://vuejs.org/guide/extras/reactivity-in-depth.html)96- [Vue.js toRaw() API](https://vuejs.org/api/reactivity-advanced.html#toraw)97