ref vs reactive in Vue 3?

Vuejs3Vue Composition-Api

Vuejs3 Problem Overview


Looking at some examples of some people's preview tutorials for Vue 3. [Currently beta right now]

I've found two examples:

Reactive

<template>
  <button @click="increment">
    Count is: {{ state.count }}, double is: {{ state.double }}
  </button>
</template>

<script>
import { reactive, computed } from 'vue'

export default {
  setup() {
    const state = reactive({
      count: 0,
      double: computed(() => state.count * 2)
    })

    function increment() {
      state.count++
    }

    return {
      state,
      increment
    }
  }
}
</script>

Ref

<template>
  <div>
    <h2 ref="titleRef">{{ formattedMoney }}</h2>
    <input v-model="delta" type="number">
    <button @click="add">Add</button>
  </div>
</template>

<script>
import { ref, computed, onMounted } from "vue";

export default {
  setup(props) {
    // State
    const money = ref(1);
    const delta = ref(1);

    // Refs
    const titleRef = ref(null);

    // Computed props
    const formattedMoney = computed(() => money.value.toFixed(2));

    // Hooks
    onMounted(() => {
      console.log("titleRef", titleRef.value);
    });

    // Methods
    const add = () => (money.value += Number(delta.value));

    return {
      delta,
      money,
      titleRef,
      formattedMoney,
      add
    };
  }
};
</script>

Vuejs3 Solutions


Solution 1 - Vuejs3

Key Points
  • reactive() only takes objects, NOT JS primitives (String, Boolean, Number, BigInt, Symbol, null, undefined)
  • ref() is calling reactive() behind the scenes
  • Since reactive() works for objects and ref() calls reactive(), objects work for both
  • BUT, ref() has a .value property for reassigning, reactive() does not have this and therefore CANNOT be reassigned
Use

ref() when..

  • it's a primitive (for example 'string', true, 23, etc)
  • it's an object you need to later reassign (like an array - more info here)

reactive() when..

  • it's an object you don't need to reassign, and you want to avoid the overhead of ref()
In Summary

ref() seems like the way to go since it supports all object types and allows reassigning with .value. ref() is a good place to start, but as you get used to the API, know that reactive() has less overhead, and you may find it better meets your needs.

ref() Use-Case

You'll always use ref() for primitives, but ref() is good for objects that need to be reassigned, like an array.

setup() {
    const blogPosts = ref([]);
    return { blogPosts };
}
getBlogPosts() {
    this.blogPosts.value = await fetchBlogPosts();
}

The above with reactive() would require reassigning a property instead of the whole object.

setup() {
    const blog = reactive({ posts: [] });
    return { blog };
}
getBlogPosts() {
    this.blog.posts = await fetchBlogPosts();
}
reactive() Use-Case

A good use-case for reactive() is a group of primitives that belong together:

const person = reactive({
  name: 'Albert',
  age: 30,
  isNinja: true,
});

the code above feels more logical than

const name = ref('Albert');
const age = ref(30);
const isNinja = ref(true);

If you're still lost, this simple guide helped me: https://www.danvega.dev/blog/2020/02/12/vue3-ref-vs-reactive/

An argument for only ever using ref(): https://dev.to/ycmjason/thought-on-vue-3-composition-api-reactive-considered-harmful-j8c

The decision-making behind why reactive() and ref() exist as they do and other great information, the Vue Composition API RFC: https://vuejs.org/guide/extras/composition-api-faq.html#why-composition-api

Solution 2 - Vuejs3

There are some similarities between ref and reactive, in that they both provide a method to store data and allow that data to be reactive.

However:

High level differences:

> You can’t use reactive() on primitives (strings, numbers, booleans) - that’s what you need refs for, because you will have situations where you need to have a “reactive boolean”, for example… > > of course your can create an object that wraps the primitive value and make that reactive():

const wrappedBoolean = reactive({
  value: true
})

> and just like that, you reinvented a ref.

Source: Vue forum discussion

Reactive

reactive takes the object and returns a reactive proxy to the original object.

Example

import {ref, reactive} from "vue";

export default {
  name: "component",
  setup() {
    const title = ref("my cool title")
    const page = reactive({
      contents: "meh?",
      number: 1,
      ads: [{ source: "google" }],
      filteredAds: computed(() => {
        return ads.filter(ad => ad.source === "google")
      })
    })
    
    return {
       page, 
       title
    }
  }
}

Explanation

In the above, Whenever we want to change or access the properties of page,
say page.ads, page.filteredAds will update via Proxies.

Solution 3 - Vuejs3

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:

  • Reactivity loss:
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.

Solution 4 - Vuejs3

ref / reactive both are been used to create reactive object where the changes been tracked.

Ref :

It takes an primitives argument and return a reactive mutable object. The object has single property ‘value’ and it will point to the argument taken by it.

Reactive :

It takes a JavaScript object as a argument and returns Proxy based reactive copy of the object.

Ref vs Reactive :

Typically, ref and reactive both have been used to create reactive objects where ref is used to make the primitive values to be reactive (Boolean, Number, String). But reactive won’t work with primitives rather than it works for objects.

For further details : Refer Ref vs Reactive

Solution 5 - Vuejs3

Below you can see our example using Reactive References on the upper part, and below other alternative reactive syntax.

//reactivity with ref syntax

import { ref, computed } from vue

export default {
  setup() {
    const capacity = ref(4)
    const members = ref(["Tim", "John", "Andr"])
    const simpleComputed = computed(() => {
      return capacity.value - members.value.length
    })
  
    return { capacity, members, simpleComputed }
  }
}


//reactivity with reactive syntax

import { reactive, computed } from vue

export default {
  setup() {
    const event = reactive({
      capacity: 4,
      members: ["Tim", "John", "Andr"]
      simpleComputed: computed(() => {
        return event.capacity - event.capacity.length
      }
    })
    return { event }
  }
}

As it shows in the code above on the bottom part, I created a new event constant which takes a plain JavaScript object and returns a reactive object. This may look familiar to using the data option in our regular component syntax, where I also send in an object. However, as you can see above, I can also send in our computed properties into this object. You should also notice that when I use this syntax we no longer need to write .value when accessing properties. This is because I am simply accessing the object properties on the event object. You should also notice that we’re returning the entire event

Both syntaxes are valid for usage, and neither is deemed as best practice

Solution 6 - Vuejs3

Ref : It takes a primitives argument and returns a reactive mutable object. The object has a single property ‘value’ and it will point to the argument taken by it.

Reactive : It takes a JavaScript object as an argument and returns a Proxy-based reactive copy of the object.

You can learn more about it from this video tutorial: https://www.youtube.com/watch?v=JJM7yCHLMu4

Solution 7 - Vuejs3

ref object takes an inner value and returns a reactive and mutable object. usually used for primitive type single variables such as String, Boolean, Number, etc.

reactive is a wrapper object that takes an object and returns a reactive proxy of the original object. usually used for dictionary-structured types such as JS Object.

You can learn more about ref vs reactive in this article: https://dev.to/hirajatamil/must-know-ref-vs-reactive-differences-in-vue-3-composition-api-3bp4

Recommended Vue Composition-Api Solutions

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDenis TsoiView Question on Stackoverflow
Solution 1 - Vuejs3Chris HayesView Answer on Stackoverflow
Solution 2 - Vuejs3Denis TsoiView Answer on Stackoverflow
Solution 3 - Vuejs3RolandView Answer on Stackoverflow
Solution 4 - Vuejs3George Princelin JView Answer on Stackoverflow
Solution 5 - Vuejs3elvonkhView Answer on Stackoverflow
Solution 6 - Vuejs3Harish KumarView Answer on Stackoverflow
Solution 7 - Vuejs3Muhammad GataView Answer on Stackoverflow