--- 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)