Vue 3 introduces two main methods for creating reactive data: reactive()
and ref()
. While both approaches serve the purpose of making data reactive in a Vue application, there are important distinctions and limitations to consider when choosing between them. In this article, we’ll explore these methods with code examples and discuss the limitations of using reactive()
.
Table of contents
Open Table of contents
The Basics: reactive()
and ref()
Before delving into the limitations, let’s briefly review how to use both reactive()
and ref()
.
// Using ref()
import { ref } from "vue";
const person = ref({
name: "John",
age: 30,
weight: 180,
});
// Accessing properties
console.log(person.value.name); // Output: John
// Reassigning the entire object
person.value = {
name: "Jane",
age: 25,
weight: 150,
};
// Using reactive()
import { reactive } from "vue";
const person2 = reactive({
name: "John",
age: 30,
weight: 180,
});
// Accessing properties
console.log(person2.name); // Output: John
// Reassigning the entire object (will result in an error)
// person2 = { name: 'Jane', age: 25, weight: 150 }; // Error!
Limitations of reactive()
- Non-Primitive Types Only
One significant limitation of reactive()
is that it can’t handle primitive types. If you attempt to make a primitive value reactive, Vue will throw an error.
const number3 = reactive(3); // Error: Type can't be a primitive
- No Reassigning Entire Objects
Unlike ref()
, reactive()
does not allow the reassignment of the entire object. If you try to assign a new object to a variable created with reactive()
, Vue will raise an error.
const updatePerson2 = () => {
// Error: Cannot assign to 'person2' because it is a constant.
person2 = {
name: "Jane",
age: 25,
weight: 150,
};
};
- Loss of Reactivity in Composable Functions
When using reactive()
within composable functions (reusable logic), the data may lose reactivity. In contrast, ref()
maintains reactivity in such scenarios.
import { reactive, ref, watchEffect } from "vue";
// Creating a reactive object
const reactivePerson = reactive({
name: "John",
age: 30,
weight: 180,
});
// Creating a ref object
const refPerson = ref({
name: "John",
age: 30,
weight: 180,
});
// Composable function using reactive()
const useReactiveComposable = () => {
const localData = reactivePerson;
watchEffect(() => {
// This log statement will execute, but the reactivity may be lost in certain scenarios.
console.log("Reactive Composable:", localData.name);
});
return localData;
};
// Composable function using ref()
const useRefComposable = () => {
const localData = refPerson;
watchEffect(() => {
// This log statement will execute, and reactivity will be maintained.
console.log("Ref Composable:", localData.value.name);
});
return localData;
};
// Example usage
const reactiveComposableResult = useReactiveComposable();
const refComposableResult = useRefComposable();
// Modifying the data
reactivePerson.name = "Jane";
refPerson.value.name = "Jane";
// In certain scenarios, the following line might not trigger the watchEffect in useReactiveComposable
reactiveComposableResult.name = "Updated Jane";
// This line will always trigger the watchEffect in useRefComposable
refComposableResult.value.name = "Updated Jane";
In the above example, we create two composable functions, useReactiveComposable and useRefComposable, using reactive()
and ref()
respectively. Both functions include a watchEffect that logs the name property of the local data.
When modifying the data, you’ll notice that reactivity may be lost in certain scenarios when using reactive()
within the composable function. On the other hand, ref()
ensures that reactivity is maintained, and the watchEffect consistently triggers.
This highlights a scenario where using ref()
might be preferred, especially when dealing with composable functions that need to maintain reactivity across various situations.
Conclusion
In determining whether to use reactive()
or ref()
, consider the specific use case and limitations associated with each method.
Use ref()
when:
- Dealing with primitive types.
- Reassigning entire objects.
Consider reactive()
when:
- Working with non-primitive types.
- Accepting the limitations in reassigning entire objects.
- Avoiding potential reactivity loss in composable functions.
The Vue community debates the optimal choice between these methods, and the decision ultimately depends on the context of your application. As a general guideline, ref()
is often preferred due to its flexibility and ease of use. However, there are situations where reactive()
might be the more suitable choice.
After exploring the limitations and examples provided, you should now have a better understanding of reactive data in Vue 3 and the factors influencing the choice between reactive()
and ref()
. Choose the method that aligns with your specific project requirements and coding preferences.