If you use Vue 3 with Composition API, you've probably heard arguments of either ref()
or reactive()
being the better way to make data in your components have reactivity. In this article, I try to make it easier to understand both ways and how to use them.
When we talk of reactivity here, we mean the ability to automatically update the User Interface (UI) when the underlying data and/or variable values change. Reactivity removes the need for manual refreshing of a page in order to see the changes in data within the DOM elements.
ref()
Don't confuse this ref()
with Template Refs (ref
attribute allows us to have direct reference to a DOM element, read more on this here), though you may find yourself using Template Refs together with ref()
:
<input ref="input" />
From the Vue documentation:
ref() takes an inner value and returns a reactive and mutable ref object, which has a single property .value that points to the inner value.
Mutable simply means something thats changeable or liable to change (if you're wondering what the word means).
You can interpret the above statement as using the .value
property to make read operations tracked and write operations to trigger associated effects (using <script setup>
):
<script setup>
import { ref } from "vue";
const count = ref(0);
const user = ref({
name: "Jeff",
email: "mynameisjeff@test.com",
});
// this won't work
const increaseCount = () => {
count++;
};
//this will
const increaseCount = () => {
count.value++;
};
//for objects it would be like this
user.value.email = "jeff@example.com";
console.log(user.value.email);
</script>
<template>
<h1>{{ count }}</h1>
</template>
Using <script>
:
<template>
<h1>{{ count }}</h1>
</template>
<script>
import { ref } from "vue";
export default {
setup() {
const count = ref(0);
const user = ref({
name: "Jeff",
email: "mynameisjeff@test.com",
});
const increaseCount = () => {
count.value++;
};
user.value.email = "jeff@example.com";
console.log(user.value.email);
return { count, user, increaseCount };
},
};
</script>
Vue does unwrap the ref()
when used inside <template>
so that's why we don't need to add .value
when referring to the variable while inside <template>
.
Another important thing to note about ref()
is that when it is used with non-primitive objects (Array, Object...), it uses reactive()
under the hood and makes it deeply reactive:
// these two are almost the same
ref({}) ~= ref(reactive({}))
One can avoid making a ref()
deeply reactive by using shallowRef()
. You can read more of this in the Vue documentation.
reactive()
From the Vue documentation:
reactive() returns a reactive proxy of the object.
Basically reactive()
is similar to ref()
the difference is that reactive()
only accepts non-primitive objects (Array, Object...). Another difference is that with reactive()
, you can access object properties directly (using <script setup>
):
<script setup>
import { reactive } from "vue";
const state = reactive({
count: 0,
});
state.count++;
console.log(state.count);
</script>
<template>
<h1>{{ state.count }}</h1>
</template>
Using <script>
:
<template>
<h1>{{ state.count }}</h1>
</template>
<script>
import { reactive } from "vue";
export default {
setup() {
const state = reactive({
count: 0,
});
state.count++;
console.log(state.count);
return { state };
},
};
</script>
reactive()
can unwrap ref()
and use the values within itself i.e: you can use ref()
and reactive()
together and still have reactivity between the two like this:
import { ref, reactive } from "vue";
const count = ref(0);
const state = reactive({
count,
});
count.value++;
console.log(state.count); // 1
state.count++;
console.log(count.value); // 2
reactive()
You can destructure or spread the values of the object in reactive()
using toRefs()
like this (using <script setup>
):
<script setup>
import { reactive, toRefs } from "vue";
const state = reactive({
count: 0,
});
state.count++;
const { count } = toRefs(state); // count is now a ref and still has reactivity
console.log(count);
</script>
<template>
<h1>{{ count }}</h1>
</template>
Using <script>
:
<template>
<h1>{{ count }}</h1>
</template>
<script>
import { reactive, toRefs } from "vue";
export default {
setup() {
const state = reactive({
count: 0,
});
state.count++;
return { ...toRefs(state) };
},
};
</script>
In my opinion, the choice of whether to use ref()
or reactive()
is really your choice and it depends on a lot of the different situations you will have when developing your Vue app. The two methods are pretty much the same but have different ways in which they do something. I also think you will find yourself in a lot of situations where ref()
is used more but the choice is really yours. I hope that this article has helped you understand these two methods. You can let me know in the comments sections which one you prefer and why. Also let me know if I have made any incorrect statements in the article.
While preparing to write this article, I read these articles which helped understand more on the differences between the two methods and how to use them: