[Medium] ref vs reactive
1. What are ref and reactive?
What are
refandreactive?
ref and reactive are two core APIs in Vue 3 Composition API for creating reactive state.
ref
Definition: ref creates a reactive wrapper for a primitive value or object reference.
Click to expand basic ref example
<script setup>
import { ref } from 'vue';
// primitives
const count = ref(0);
const message = ref('Hello');
const isActive = ref(true);
// object also works with ref
const user = ref({
name: 'John',
age: 30,
});
// access with .value in JavaScript
console.log(count.value); // 0
count.value++;
</script>
reactive
Definition: reactive creates a reactive object proxy (not for primitive values directly).
Click to expand basic reactive example
<script setup>
import { reactive } from 'vue';
const state = reactive({
count: 0,
message: 'Hello',
user: {
name: 'John',
age: 30,
},
});
// direct property access
console.log(state.count); // 0
state.count++;
</script>
2. ref vs reactive: Key Differences
Main differences between
refandreactive
1. Supported types
ref: works with any type.
const count = ref(0); // number
const message = ref('Hello'); // string
const isActive = ref(true); // boolean
const user = ref({ name: 'John' }); // object
const items = ref([1, 2, 3]); // array
reactive: works with objects (including arrays), not primitives.
const state = reactive({ count: 0 }); // object
const list = reactive([1, 2, 3]); // array
const count = reactive(0); // invalid usage
const message = reactive('Hello'); // invalid usage
2. Access style
ref: use .value in JavaScript.
Click to expand ref access example
<script setup>
import { ref } from 'vue';
const count = ref(0);
console.log(count.value);
count.value = 10;
</script>
<template>
<div>{{ count }}</div>
<!-- auto-unwrapped in template -->
</template>
reactive: direct property access.
Click to expand reactive access example
<script setup>
import { reactive } from 'vue';
const state = reactive({ count: 0 });
console.log(state.count);
state.count = 10;
</script>
<template>
<div>{{ state.count }}</div>
</template>
3. Reassignment behavior
ref: can be reassigned.
const user = ref({ name: 'John' });
user.value = { name: 'Jane' }; // valid
reactive: should not be reassigned to a new object variable binding.
let state = reactive({ count: 0 });
state = { count: 10 }; // loses reactivity connection
4. Destructuring
ref: destructuring ref.value gives plain values (not reactive).
const user = ref({ name: 'John', age: 30 });
const { name, age } = user.value; // plain values
reactive: direct destructuring loses reactivity.
const state = reactive({ count: 0, message: 'Hello' });
const { count, message } = state; // loses reactivity
import { toRefs } from 'vue';
const refs = toRefs(state);
// refs.count and refs.message keep reactivity
3. When to use ref vs reactive?
When should you choose each API?
Use ref when
- State is primitive.
const count = ref(0);
const message = ref('Hello');
- You may replace the whole value/object.
const user = ref({ name: 'John' });
user.value = { name: 'Jane' };
- You need template refs.
<template>
<div ref="container"></div>
</template>
<script setup>
const container = ref(null);
</script>
- You want consistent
.valuestyle across values.
Use reactive when
- Managing complex object state.
const formState = reactive({
username: '',
password: '',
errors: {},
});
- Grouping related fields together without replacing object identity.
const userState = reactive({
user: null,
loading: false,
error: null,
});
- You prefer direct property access for nested structures.
4. Common Interview Questions
Common interview questions
Question 1: basic differences
Explain outputs and behavior:
// case 1: ref
const count1 = ref(0);
count1.value = 10;
console.log(count1.value); // ?
// case 2: reactive
const state = reactive({ count: 0 });
state.count = 10;
console.log(state.count); // ?
// case 3: reactive reassignment
let state2 = reactive({ count: 0 });
state2 = { count: 10 };
console.log(state2.count); // ?
Click to view answer
console.log(count1.value); // 10
console.log(state.count); // 10
console.log(state2.count); // 10 (value exists but no longer reactive)
Key points:
refrequires.valuereactiveuses direct property access- reassigning
reactiveobject binding breaks reactive tracking
Question 2: destructuring pitfall
What is wrong here and how to fix it?
// case 1: ref destructuring
const user = ref({ name: 'John', age: 30 });
const { name, age } = user.value;
name = 'Jane'; // ?
// case 2: reactive destructuring
const state = reactive({ count: 0, message: 'Hello' });
const { count, message } = state;
count = 10; // ?
Click to view answer
Case 1 (ref):
const user = ref({ name: 'John', age: 30 });
const { name, age } = user.value;
name = 'Jane'; // does not update user.value.name
// correct
user.value.name = 'Jane';
// or
user.value = { name: 'Jane', age: 30 };
Case 2 (reactive):
const state = reactive({ count: 0, message: 'Hello' });
const { count, message } = state;
count = 10; // loses reactivity
// correct approach 1
state.count = 10;
// correct approach 2
import { toRefs } from 'vue';
const refs = toRefs(state);
refs.count.value = 10;
Summary:
- destructured plain values are not reactive
- use
toRefsfor reactive object destructuring
Question 3: choose ref or reactive
Choose API for each scenario:
// Scenario 1: counter
const count = ?;
// Scenario 2: form state
const form = ?;
// Scenario 3: user object that may be replaced
const user = ?;
// Scenario 4: template ref
const inputRef = ?;
Click to view answer
const count = ref(0); // primitive
const form = reactive({
username: '',
password: '',
errors: {},
}); // grouped object state
const user = ref({ name: 'John', age: 30 }); // easier full replacement
const inputRef = ref(null); // template ref must use ref
Rule of thumb:
- primitive ->
ref - full-object replacement needed ->
ref - complex grouped object state ->
reactive - template refs ->
ref
5. Best Practices
Best practices
Recommended
// 1) primitives with ref
const count = ref(0);
const message = ref('Hello');
// 2) structured object state with reactive
const formState = reactive({
username: '',
password: '',
errors: {},
});
// 3) use ref when full replacement is common
const user = ref({ name: 'John' });
user.value = { name: 'Jane' };
// 4) use toRefs when destructuring reactive object
import { toRefs } from 'vue';
const { username, password } = toRefs(formState);
Avoid
// 1) do not use reactive for primitives
const count = reactive(0); // invalid
// 2) do not reassign reactive binding
let state = reactive({ count: 0 });
state = { count: 10 }; // breaks tracking
// 3) avoid direct destructuring of reactive when you need reactivity
const { count } = reactive({ count: 0 }); // loses tracking
6. Interview Summary
Interview summary
Quick memory
ref:
- any type
.valuein JavaScript- easy full replacement
- auto-unwrapped in template
reactive:
- object/array only
- direct property access
- keep object identity
- use
toRefswhen destructuring
Selection guide:
- primitive ->
ref - replacement-heavy object ->
ref - grouped object state ->
reactive
Sample answer
Q: What is the difference between ref and reactive?
refwraps a value and is accessed via.valuein JavaScript, whilereactivereturns a proxy object with direct property access.refworks with primitives and objects;reactiveis for objects/arrays. Reassigningref.valueis fine; reassigning areactivebinding breaks tracking.
Q: When should I use each?
Use
reffor primitives, template refs, and object states that are often replaced as a whole. Usereactivefor complex grouped object state where stable object identity is preferred.