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.
Related
I have a component that has an <input> with a :value set to a certain property. The component has another property unrelated to this input, which can get updated asynchronously (AJAX call). Whenever you're typing inside the input and the asynchronous call finishes, updating the other property, your typed input is reset.
To recreate this problem I've created a jsfiddle using a setInterval to simulate the async call and increment the other passed property. Try typing in the input, it will get reset every second. If you're quick enough, you can tab out and cause the #change to trigger the actual update.
The question is: why is the update to the other prop invalidating/rerendering the component and how can I work around this?
Note that v-model="person.name" is not a valid solution here - I need to know the old and new value, which is why I'm using a manual :value/#change combo.
Edit: The updateName method also really only needs to be triggered when the user leaves the input field. This is because the code run inside it is relatively CPU intensive and only needs to run when the user is done with the input and leaves it (in my actual code, not the jsfiddle).
Edit2: Is there some way to not let it re-render the entire component, but only the relevant pieces?
Because the parent component is changing a property of the child component, it has to re-render (parts of) the child component. Since you are using #change, instead of #input, your changes are not saved yet to the reactive variable person.name, it only works if you click tab quick enough. One solution would be to change #change to #input (which better resembles v-model):
https://jsfiddle.net/mf67xq1e/
Another (better) option is to use v-model and use a watcher to retrieve both the old and the new value:
watch: {
// whenever person.name changes, this function will run
"person.name": function (newName, oldName) {
console.log("newName:", newName);
console.log("newName:", oldName);
}
}
https://jsfiddle.net/mf67xq1e/1/
EDIT:
As you mentioned you only need to trigger something when you blur/leave the input field, seperate the reactivity of the variable and the triggering of your other method (e.g. updating some other variable or something), in two seperate variables:
https://jsfiddle.net/ta9cgnx0/
EDIT 2: Cleaner option with v-model and a seperate call for your other trigger on blur:
https://jsfiddle.net/ay1g63u8/
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()
I need pass the main component to a deeper child. Currently I am doing on child this.$parent.$parent, but is not reliable for me, because this same child could be in a third+ deepth level.
My idea is transfer the main component reference between childs, like:
<v-some-child :main-component="_self"></v-some-child>
So I can use the main component instance where I think need by passing the _self or this.mainComponent data to a more inner child.
My doubt is:
_self attribute is reliable to points to component Vue instance? Seems that _ prefix is to internal attributes, and $ for public (reliable). If it is true, what I can do use?
There are some better way to do what I wants?
Thanks!
Edit 1: as an immediate more reliable workaround, I am using data() { self: this }, then I am using :main-component="self".
I'm unclear where _self is getting set in your case. Typically _self = this; is an approach used by developers to pass the the current Vue instance via closure to a callback function. I wouldn't trust a _self unless I knew exactly where it was getting set.
Your edit1 should absolutely work. As long as data(){ self: this } is happening on the main Vue instance and in the components you use self when setting a property via a : like :main-component="self" then that should work fine given that the main Vue component data properties are merged into all components used by that vue instance.
It's kinda a cleaver approach frankly. As #raina77ow mentioned it may make sense to look at VueX or an EventBus but the approach you propose in Edit1 should be a reliable way to pass around the main Vue instance.
As an aside, I wonder if root or main might be a better name then self. Given that it's the "self" of the main or root level Vue instance not the the "self" of the child passing the value.
I'm discovering VueJS and I don't understand exactly the differences between updated and watchers.
Updated hook
It is a lifecycle hook. According to the official documentation, it is triggered whenever data changes. So whenever a prop or a data is updated (the value, not only the pointer), updated is called.
Watchers
In the documentation, watchers are compared to computed properties. But in which cases would it be best to use updated instead of watchers ?
It seems that in both cases, DOM is not updated when the callback is called (nextTick() is required if we want to manipulate the changes in the DOM). The only difference I see is that watchers are only triggered when one precise property (or data) is updated where updated is always called.
I can't figure out what are the pros of updating whenever a data changes (updating) if we can be more accurate (watchers).
Here are my thoughts.
Thanks :)
The lifecycle hooks around update respond to changes in the DOM. Watchers respond to changes in the data. DOM changes are generally in response to data changes, but they might not be data owned by the component in question. One example where updated could be used is noticing that slot content has updated.
I think a better analogous lifecycle hook to the watchers may be the beforeUpdate hook. The updated hook is called after the virtual DOM has re-rendered, whereas beforeUpdate is called before the virtual DOM has re-rendered. You can see a visual representation of this on the diagram you linked to.
in which cases would it be best to use updated instead of watchers ? (...) I can't figure out what are the pros of updating whenever a data changes (updated) if we can be more accurate (watch).
The documentation says that you should prefer a watcher or computed property instead of updated if it is possible to achieve your goal that way. My understanding is that the updated hook was included to allow users to watch for any changes to the virtual DOM (see below).
Here's the explanation from the Vue 2.0 release notes on watch vs. updated:
User watchers created via vm.$watch are now fired before the associated component re-renders. This gives the user a chance to further update other state before the component re-render, thus avoiding unnecessary updates. For example, you can watch a component prop and update the component's own data when the prop changes.
To do something with the DOM after component updates, just use the updated lifecycle hook.
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