I will simply explain why there are 2 ways of creating a reactive state:
Other answers already show the differences between the two
reactive
: Create a reactive state. Returns a reactive proxy of the object:
import { reactive } from 'vue'
const reactiveObj = reactive({ count: 0 })
reactiveObj.count++
With Options API we used to keep reactive state in data()
. With Composition API we can achieve the same with reactive
API. So far, so good, but...
Why do we need ref
???
Simply because reactive
has limitations such as:
const state = reactive({ count: 0 })
// the function receives a plain number and
// won't be able to track changes to state.count
callSomeFunction(state.count)
const state = reactive({ count: 0 })
let { count } = state
// does not affect original state
count++
let state = reactive({ count: 0 })
// this won't work!
state = reactive({ count: 1 })
- It cannot hold primitive types such as string, number or boolean.
So ref
, was provided by Vue to address the limitations of reactive
.
ref()
takes the argument and returns it wrapped within a ref object with a .value property:
const count = ref(0)
console.log(count) // { value: 0 }
console.log(count.value) // 0
count.value++
console.log(count.value) // 1
Refs can:
- hold any value type
- reactively replace the entire object:
const objectRef = ref({ count: 0 })
// this works reactively
objectRef.value = { count: 1 }
- be passed into functions or destructured from plain objects without losing reactivity
const obj = {
foo: ref(1),
bar: ref(2)
}
// the function receives a ref
// it needs to access the value via .value but it
// will retain the reactivity connection
callSomeFunction(obj.foo)
// still reactive
const { foo, bar } = obj
Should I always use ref
?
Personal opinion follows
Most devs who have tried both, suggest using ref
from articles that I have read.
But personally, I think that ref
has the same limitation as reactive
if not used correctly and you can easily fall into "Reactivity loss" issues.
ref
has also some behaviors like:
- unwrapping in templates but that happens only to top-level properties
- unwrapping inside
reactive
- no unwrapping is performed when the ref is accessed from an array or a native collection type like Map
- Synchronization of refs
Also having to deal with .value
every time is a bit confusing, Vue knows that and there is an RFC - Reactivity Transform as of this time of writing that aims to provide a solution.
I hope you now have a better understanding of reactive
and ref
but I think is worth mentioning that there more APIs for reactive state that you should be aware of: readonly, shallowRef, shallowReactive, shallowReadonly, unref, and many more.