This article won't do a deep dive into the implementation of Vue.js reactivity, I just want to show some examples that will break the reactivity when you are writing with the framework.
Should you be interested in the mechanism of the reactivity under the Vue.js, here are some information I think that would help:
TL;DR
- We cannot use
toRefs
to modify aref
's value that stands for a primitive value - Destructuring
props
breaks its reactivity. - Mutable methods are totally acceptable when using Vue.
Two Way Binding
Let's start with a simple input element with a reset button, we can see what we have typed down below the input element.
It works! Not surprising, right? But in reality, we frequently separate elements into child components to make the function of each component clearer. So, let's move the input element and the reset button to Comp.vue
. And it seems Vue is arguing about the prop are read-only.
It's good to see Vue prohibit modifying props "directly", because it usually causes data flow of the states to become a mess. BUT you have noticed that I double quoted the "directly" right? By using toRefs
, we can transform and destructure properties in the props to ref
s that is belong to the child component. And...it fails, it turned out you still cannot modify a ref
's value that is "primitive". Just "primitive", let's make our ref-- msg
, to become an object: ref({ greetings: 'Hello World!' })
, and it's work again!
We cannot use toRefs
to modify a ref
's value that stands for a primitive value, but we can change the primitive values of the properties in a ref
that acts for an object.
How about to destructure the props
deeper to the msg
that have we type less codes without the object but just its property-- greetings
. Let's type something in the input, it worked...before clicking the "Resume to default" button. (I also add styles to stand out the elements π)
Destructuring props
breaks its reactivity. Though we use toRefs
, the ref
in the child component is no longer synchronized with the ref
in the parent component via props
.
Immutation and Mutation
For this part, let's play both mutable and immutable method on the object ref
to see how we can break the reactivity.
Again, on List.vue
, clicking "Add to ordered list" button will trigger a function that is trying to mutate the value of a ref
that is an array itself, but not the element in it, which cause a warning pops up.
As what we learned previously, putting the orderedList
as a property of lists
' ref solves the issue. However, there is another way-- mutable methods. By means of the methods, I will not change what the value
property under the ref
points to, so I do not need to put unorderedList
into the lists
object to add items into the list.
Unlike states which are all immutable in React, the Vue doc mentions that mutation on
ref
is valid-- Reactivity API: Core #ref | Vue.js.
Yet currently the functions to add items to or clear the content of the lists in List.vue
are a little bit duplicated. Using dependency injection, we can make the functions more flexible. And the reactivity breaks again...
In the case of assigning a new array to the argument list
, we didn't change the lists.value.ol
points to, but the reference of the argument list
:
Here is an example provided by the people in Vue community to show the scope of the argument.
let a = [];
function add(b) {
b = [...b, 1];
console.log(b) // [1]
}
add(a);
console.log(a); // []
Though it's okay to do:
function addToList(item) {
lists.value.ol = [...lists.value.ol, item];
}
or
// pass the whole lists
function addToList(lists, item) {
lists.ol = [...lists.ol, item];
}
For flexibility, I would choose to use mutable methods.
Just one more thing...
I don't know the actual mechanism, but it seems Vue triggers the effects when the value of a ref
is changed, and then to update the peer dependencies of ref
s in the component.
What I know is that Vue uses "signal" to have the reactivity, and its concept may refer to observer pattern
The example below use shallowRef
instead of ref
for the lists
. You can see both add to list button don't make changes on the screen. But if you then click the "restore to default" button, the added items show up suddenly.
It's also work without shallowRef
but a plain object. And it won't do so if the functions in the List.vue
no longer use the msg
.
Thanks for reading π
Top comments (0)