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