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.