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
Related
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.
As you know, for each data property, there's a new Dep class created. Each Dep class has subscribers of watchers.
I looked through vue.js source code and for each component, there's only ONE watcher class being created which also holds the render function(template of the component).
1) Could you describe the situation when and why data property of one of the components might have the Dep class which has more than one watcher class?
2) So can I sum up something like this: if we have a component which has 5 data properties. each of these 5 data properties has different Dep class instance. and each of those Dep class has the same Watcher and that watcher holds the component's render function. If state changes, one of those 5 Dep class's notify gets run and that notify runs its watcher's render function?
You may find this introduction useful:
https://medium.com/dailyjs/tracing-or-debugging-vue-js-reactivity-the-computed-tree-9da0ba1df5f9
As you've mentioned, each data property will have its own instance of Dep. Each Dep has a subs property that holds an array of subscribers. The subscribers in that array will all be instances of the Watcher class. If a data property changes then the corresponding Dep will notify each Watcher in its subs.
Each Watcher also holds references back to its dependencies, in a property called deps. This is just an array of the Dep objects to which the Watcher is subscribed.
You'll be able to see this in your browser developer tools. If you log a Vue instance you'll find a property called _watchers that holds all the watchers related to that component. Expanding their deps will lead you to the Dep objects, though it can be tricky to tell exactly which data property each Dep represents.
The rendering process has a Watcher that it uses to track its data dependencies. You'll only get one of those per component instance.
A Watcher is also created if you use watch or $watch. Again you'll be able to see these in _watchers.
Computed properties have one Watcher each. Those will be present in the _watchers array but it's easier to see them in _computedWatchers.
A key thing to note is that dependencies are flattened. If you use a computed property you'll actually just get a dependency on all the data properties that contributed to it. You can't form a direct dependency on the computed property itself.
So to go back to your original question:
Rendering, computed properties and watch will all contribute to the subs of a Dep. Computed properties will often contribute more subscribers than you might expect due to the flattening of the dependencies.
Almost. The render function isn't called directly. When the Watcher for rendering is notified of a data change it will actually just add the component to a queue. That rendering queue won't be processed until the start of the next tick.
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'm using the Objective-C API, though I would imagine that this question is valid regardless of language.
I'm trying to write my code so that my app observes when child objects are removed from a particular node. When a child object is removed, I make local changes so that the local version of that child object is deleted.
I also have observers set for each child object's children on Firebase. This is so that I can propagate changes to those values locally as soon as they are made.
My observers are (finally!) working just fine – except that when I delete an object from Firebase, it seems like the observers for the object's children are triggered before the observer for the object's parent. E.g.,
If I have several blog posts listed under $userId/blogs/ according to their $blogId, and if each blog post has properties such as name and text, it seems like the FEventTypeValue observers at $userId/blogs/$blogId/name and $userId/blogs/$blogId/text are triggered before the FEventTypeChildRemoved observer at $userId/blogs when I set a particular $blogId to nil.
Any idea how I can set it so that the FEventTypeChildRemoved observer triggers before the FEventTypeValue observers? The alternative would be to always check whether the parent object exists when observing a change to a property on Firebase, but this to me seems like overkill.
I've got an existing SPA that was developed using nested RactiveJS components. Which is great, and offers a ton of flexibility throughout the entire app. Currently I attempting to add in client side routing support using page. My navigation switches out high-level components using simple {{#visible}}{{/visible}} template markup on each component. This is a little troublesome in its current state as it always kicks off a re-render whenever the high-level component becomes visible again.
Is there a way to render a component, for example, called widget, without using the
<widget></widget>
method? I've already "registered" the component with the parent, but obviously when constructing it by means of
new App.components.widget
I am able to control how/when it's rendered/inserted/detached, but lose the recognition in the application's component hierarchy.
There is insert exactly for that. You don't even need to "register" it to the component you plan to put it to. You can use the different find* methods or nodes to easily retrieve a reference of your planned container element.
var instance = new YourDetachedWidget({ ... });
instance.insert('#your-container'); // This could be a node, selector or jQuery object