Clarification about Vue Reactivity System - vuejs2

I always wanted to ask the following questions regarding the Vue Reactivity System.
I have read in Vue docs that it is recommended to keep the data of components as plain JS objects over being class objects. Same goes for individual properties of components data.
Why is this recommendation?
What is the problem with having data as instances of class objects?
What edge cases are caused by using getters/setters and methods inside of component data? (this is my main question here)
I have also another question about Vue Reactivity System.
Can I directly change component computed getters/setters and methods at runtime without having problems with Vue Reactivity System?
Are changed computed getters/setters still be cached and optimized?
Are changed methods going to be accessible from component template?
Can I add new computed getters/setters and methods at runtime? How?
Thank you very much!

Vue reactivity system (but i suppose i can extend it to all other reactive framework/lib/whatever) is just an implementation of the observer pattern . In the specific case, the observer function is fired when a property value (data, computed getter result, vuex state) changes. if the property is an integer and its value is 2, is easy for vue to check out that at some point of time it has become '3', after a comparison, fired with a mutation, a vdom change etc...and this is same for other primitive types. When structured object come into play, the comparison between values is still made, but this time the value will be a reference to the object. If the reference doesn't change, vue cannot know that something has changed, so just doesn't react....
if you set val 3 = 'foo' before and val 3 = 'bar' after, the 'val' array is still the same object in the same heap area, so you should clone it, or better use Vue.set()

Related

VueJS - Update a parent computed property after a child component modifies prop data

I have a relatively complex API request object I need to make, with a large number of UI components responsible for updating different properties of the object.
I'm passing the basic request model as a prop from a parent component to its children, which pass it on to theirs (down several "generations").
At the parent level, I have a computed property that returns a field of this data model, and a watch on that computed property.
When a child component updates the property on the model, it successfully updates everywhere that has a reference to it, but the computed property on the parent fails to recalculate, and resultantly the watch never activates.
I'm guessing I've missed the point somewhere along here, but I can't think about how else to update without resorting to long event chains through the UI.. How should I be approaching this instead?
To anyone with a similar question - from my research it seems that modifying reference values on props is not the intended approach for VueJS. Which is a shame, because initially it seemed like quite a neat pattern.
I've implemented vuex now, which is working well, and avoids long lines of events going back to the original owner of the prop data.
IF you wanted to press it, then modifying references on the object itself will force updates down the chain. So (e.g.) if you wanted to update an array property of the prop data, then instead of "pushing" to it, you would replace the whole array object (causing other components with computed properties on that array property to recalculate). But again, not recommended.

Vue.js list rendering diff: "same object" vs "has same values"

I have just hit a reactivity problem in Vue.js (2.6.12) component. It was not re-computing and re-rendering after its props were updated. The issue is that the component is used in a list with v-for with a key. The key was not changing, but the object's data was changing i.e. it was the same object, but its data was different.
The key is used as an optimization for diffing the list in v-for so it can re-use and re-order the same components without re-creating them. My original key was the object's id and the solution is to create the key from the id and all the object's changing data. That leads to other issues, like losing the component's internal data state, which has no need to be stored in a Vuex store.
My question is, is there a way in Vue.js (maybe in v3?) to tell in v-for not only if the object is the same (via key using object's id) but also if the object has the same data? Which will update the prop and re-computed and re-render the component without losing its internal data?
Funny thing is that the component's props are updating, it just does not re-compute and re-render. Verified via the Vue Devtools in a browser.
EDIT:
Here is an example where the reactivity does not work. It is using a Vuex store and the array for v-for is a computed property, which joins two properties from the store.
I just figured out that the problem is the joining of the values via the map on line 67: o.number = extras[o.id]. After replacing this with Vue.set(o, 'number', extras[o.id]) it is working!
This problem doesn't exist as described. Here is a small example of object reactivity working properly: https://jsfiddle.net/sh0ber/04s1jt8g
I'm guessing, however, that you're encountering the change detection caveat and need to use Vue.set.
The reason is now clear with the code example: Your computed creates a new property (number) on each Vuex obj that's not defined in Vuex at the time you set objs. If you had initialized the objs to have a number property as well, you wouldn't need Vue.set. Another way to prove it is to set o.text instead of o.number and you'll see it's reactive without Vue.set
(Btw, it makes more sense to do this computation in a Vuex getter instead of a computed; getters are computeds for Vuex. It's nice to decouple the logic from the component, and imagine you needed the result in multiple components)

Does each child add reactive getters and setters to props?

Imagine you have a huge list of things to do:
[
{
id: 1,
name: Pet a cat,
priority: 'extreme'
},
...
]
The app has three components grandparent -> parent -> child
The todo list is declared in grandparent's data function. This is where the list becomes reactive first. The list is then passed from grandparent to parent and from parent to child as a prop. From my limited understanding each component in the chain adds their own getters and setters. My concern is that this way of passing props is not optimal for performance. I mean, should I restructure my code in a way that minimizes such props passing or not?
First of all, Props in Vue are readonly. You'd get a runtime error if you ever try to update one. So actually there's no setters for parent and child components, only for grand-parent.
If your child components wants to update it, you'll have to send events until some component can actually update the data.
Second, you won't have any performance issue with it, it's the way Vue works and it's good at it. Actually it's the proper and most straightforward way to achieve what you want. Obviously, if the parent / child list extends even more, it's gonna be a pain for you to use only props + events, but no performance issue I think.
The other solution to avoid data to be passed through each component descendent is to use a "Vuex Store". This is not super easy to set up and understand for beginners though. You may give it a try if your app is becoming more complexe.
I'd suggest you to stick with your current solution as it has nothing wrong.
Happy coding :)

Vue Component - How to handle non-reactive data?

I'm losing track of reactivity overhead best practices in a Vue 2 Component. I need to generate a one time string with genId() and assign it to the component' id attribute. It seems like overkill to have any watching going on after that.
Is :id="myID" the right way to insert this into the html id?
And then when setting up the source where do I put the non-reactive data? I had 3 thoughts:
Add property myID: genId() to data. But doesn't that add it to the watchlist automatically even though it won't change? Causing overhead?
I read on Vue Forum from a year old answer that myID: genId() should go in the created hook. Are using hooks for this kind of thing a best practice? I thought hooks were discouraged.
Or I could put it in the component methods and then call it directly with :id="genId()
Is there a Vue way to do this?
Use method 2 for non-reactive data (and you use that component many many times on the page that the small overhead of adding the change listeners has any impact.)
created() {
this.myId = genId()
}
The 3 methods behave differently:
Method 1: data
This will call the genId() when the data object is created and add change listeners.
Method 2: created hook
This will call the genId() when the component object is created and doesn't add change listeners.
Method 3: method
This will call the genId() every time the template re-renders. Which happens every time a change is detected on a variable the view is listening to or a $forceUpdate() is called.
Ps. Vue objects already have a unique id: this._uid
but this is a private property and might change in a future version of Vue.

Reacting to changes within observed object's unobserved properties

I have an external library with TypeScript class instances in observed variable of my app's store:
class Store:
#observable active_obj:ExternalClass;
This instance has a method which, when called, updates its internal state leaving the object as is:
store.active_obj.change()
Now, since Mobx is not observing this object itself nor its (I assume) properties, only changes happening directly on the field active_obj (like new value assigned to it). Since instances of MyClass are provided by external library and ultimately get rendered inside this library's components I can't add observables and observers to its class structure nor React components. Mind you, this is only what I think is the reason that changing the object's properties doesn't trigger re-render...
I had to cheat a bit by using other, observed variable I change directly with nonsense data at the same time I'm calling to unobserved instance for change. Adding references to this variable on components higher up the tree, I can trigger re-render that produces updates on the (unobserving) components of the external library.
My question is, how best to make Mobx aware of change so it can notify observers of store.active_obj instance?
I think this part of Mobx documentation warns about this, but there's no workarounds or solutions for it:
** If likes where objects instead of strings, and if they were rendered by their own Like component, the Likes component would not rerender for changes happening inside a specific like.
from here, at end of the page
Edit
As per #mweststrate's question, I provide some context:
My app controls its data, but it has to create external class' instances from that
Instance's data is encapsulated and mutated in place, but it's done by asking from my app's side through user triggered events (meaning, I know when data is updated)
Basically class uses app's data to provide different views into data based on user selection and renders it with its React components
I also use this changed data elsewhere in the app in components I control
Changed data is part of external class' internals and I can't depend on it
Since Mobx tracks mutations it can see, using Observable doesn't directly work
Some possible solutions I thought:
manually notify observers that observable active_object has changed when I have called the instance it references to change
have a container object that Mobx can track and when I change its sentinel property, that update is noticed and actual instance with it