-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Derived arrays in MobX #166
Comments
Hi @nardi, Currently approach I have planned to add special filter, map, slice etc operations that returns observable arrays as that would potentially result in huge performance benefits, but so far nobody really needed it yet as the computed arrays are usually already fast enough. The reason for that is probably that most people use observable arrays to power their react components, and React would not benefit directly from smartly updating observable arrays in all cases, as it will always map the complete list to re-create child components (see for details this SO question). That being said, I did some successful experimenting in the past with smart map and filter functions that intelligently update the result array only for the changed items and I plan to add this feature to MobX sooner or later. If you need it, sooner ;-). A bit elaborate so I hope this answers your question, otherwise just let me now. |
@mweststrate I would love to see these derived arrays in mobx. It would help with awfully big arrays, which are cumbersome to reiterate on each small change. |
Give me a few weeks, but I'll definitely plan on adding this. Which Op wo 30 mrt. 2016 om 12:34 schreef Jiri Spac [email protected]:
|
@mweststrate thanks, order of importance for me: |
For me it would be filter, map, sort. I don't really see slice as important, nor am I sure what the semantics would be - I my case I wouldn't care about the actual numerical index of elements, as long as they are stored in unmodified order (except for sort) and enumerable. |
|
Just wondering how would it improve perf? Is it similar to view vs materialized view in SQL? |
@AriaFallah yeah it is very similar to that indeed. Instead of always remapping the array, the splices can be observed and applied (after mapping) to the target array. Etc. I made something similar in the past and it was quite doable and very efficient. Only nasty one is sort, but still doable I think with binary search / insertion. |
If you base MobX observables over xstream or even rx all these problems go away, just saying. |
That sounds interesting. Could you elaborate? |
@benjamingr no it doesn't relate, streams map events in time, this is about mapping values in space. Simply put: const todos = ["fetch coffee", "fetch tea"]
const upperCaseTodos = todos.map(s => s.toUpperCase()) MobX already has the means to already reflect the changes in |
@mweststrate I've done some work like this using CanJS's computes and red-black binary trees in the canjs/can-derive project. Specifically on the Unfortunately, I didn't achieve the performance gains I was hoping for: Be proactive, not reactive – Faster DOM updates via change propagation I'm stoked to learn how your approach compares. |
I think my issue is similar: I would like that a computed function returning an array can be observed like observable arrays, i.e., that I can observe if some elements are added/removed from the computed array. |
Mmmh, just saw that the observe method of ObservableArrays actually just conform to the (former) ES7 proposal but does not allow to observe what was added/removed, so that would actually be my first wish... |
@aravantv nope that knowledge is already available, additions and removals are just expressed in the form of 'splices'. You could achieve roughly what you want by doing something like: var source = observable([])
var derived = observable([])
source.observe(change => {
// inspect the splice / update and update the relevant parts of the derived array
}) Depending on your computation that might either be trivial or a very complicated thing to do :) |
I'm incredibly excited for these new smart map, filter, slice etc. array functions. What's the status those features? Seems like it would make changing and optimizing big data sets without doing much work. Also will be quite powerful optimizing createTransformation even further |
I'm still planning on this, but not in the near future (next month) as there don't seem to be much need for these kind of optimizations so far. If in dire need, a case specific solution using splices will often not be that hard (a generic solution is harder, as there need to be clear semantics on the influence of transactions, arbitrary observables used in mapping functions etc, but I already build something similar in a predecessor of MobX) |
I created a gist for a linked filtered observable array function: |
Hello, any news about this topic? |
Just going to add my 2 cents to this as well, array.filter would be awesome if optimized, @computed doesn't work too well for us when we start to deal with thousands of items. |
is there a best practice workaround for this while we continue to wait and wish? |
I am also curious about this. Just getting started with mobx and I'm not sure of the best way to get observabledata -> filtered observabledata -> my react component. |
@AndrewMorsillo it is ok to use the filtered ObservableArray, which is just a normal array, as props input to a React component; it will use the completely new array reference that is returned when the ObservableArray updates, to rerender the view (which does diffing on the virtual DOM, so if your array doesn't contain thousands of entries it should be OK) |
@laurensdijkstra Yes, but doesn't that mean that the component will re-render if any property of any item in the parent array is updated? Maybe I'm not fully understanding the mobx observation model yet, but it would be preferable to only have a component update if one of the referenced properties of the items in the filtered array is updated. |
@AndrewMorsillo I am not laurensdijkstra, but I map the (problematic) array to a list of observer components during render, and these observer components only get a reference to a single item. This way these observer components do not rerender when not needed as their props didn't change. Make sure to pick keys based on the item, and not on the item's index. Still not ideal, but combined with virtualization, it seems to be good enough for now for me. |
@laurensdijkstra / @kalmi, when using mobx in the standard way, mobx already will totally track changes to properties, assuming the object is observable and the child component is ready. For such scenarios the feature |
I have got similar problem when I was trying map array to mutate some properties inside callback like example show : it looks like the @computed is not listening when you mutate array in service callback @action
}; |
Couldn't you use smart mobx function to map jsx children from the computed which would then rip the benefits of granular reactivity? |
I'd like to be able to create synthetic "view models" which are computed by drawing on information from different parts of a normalized model. For a very simplified example, export class ListModel {
@observable items = [];
@observable lowerBound = 0;
@computed
get viewItems() {
return this.items.map(item => observable.object({
get id() { return item.id },
get faded() { return item.id < this.lowerBound },
}));
}
}
export class ItemModel {
constructor(id) {
this.id = id;
}
} I would like to render an array of react components based on The only workarounds I know of to this problem currently are
Having Here's a live example. |
@pelotom I went through the same journey recently. Because I didn't want to introduce any sort of circular dependencies in my stores, I tried a couple of things. I used createTransformer, but I ran out of time and I decided to not care until I hit the wall in terms of performances. Have you measured the actual impact of re rendering and see any benefit in using a transformer? As far as I can understand, the collection should still need to be re-render but not necessarily each item since they are "cached". |
@jraoult thanks for the link, I wasn’t aware of |
Ok, using
And only absolutely necessary rerenders are being performed 🎉 See the updated example at https://codesandbox.io/s/x357o6pmjz if interested. Thanks again for the tip, @jraoult! |
@pelotom this looks like a nice solution, thanks for sharing. One thing I don't understand though - why does |
@stewartml |
@pelotom i was interested in what you accomplished w/ create transformer and dug into your codepen, but something wasnt making sense to me and i couldnt put my finger on it. well i adjusted the code a bit, removed the viewmodel layer completely and removed createtransformer and im getting the hoped for result all the same, only rerenders individual components as needed: https://codesandbox.io/s/jj3rn4xzq9 im not doing this just to nitpick, im genuinely trying to understand how to work with derived arrays, and possible performance benefits. in this particular case im seeing a level of abstraction that i often use (creating an intermediary viewmodel) but im not seeing how this is needed or illustrating any benefits i this case... or am i missing something here? |
@cellvia Play around with these two (based on yours, and pelotom's codepens) and watch the console output. Search for |
ok @jumika less calculations of the computed, got it. i was only looking at number of re-renders which i imagine as the "heavier" thing, but perhaps not always. |
Sorry to resurrect this thread, but using |
Whoa, thread from 2016 and it's still alive :) I will close this for now and unless there some useful conclusion or solution, it will get locked eventually. It would be probably best to start a new discussion if there is such a need. |
+1 for this one as needed. :) At least a handful, simple utils for this reason:
For offline first apps this can be quite important I think. |
What is the best way to define a derived array in MobX? By that I mean, if I have an observable array
var a = observable(['foo', 'bar']);
and I then perform an operation on it such as
how do I do this in a way so that:
b
is an ObservableArray itself'foobar'
toa
also updatesb
to['foo', 'foobar']
It seems as if I either have to construct a new observable array with the contents of a.filter(...) to have 1 but not 2, or I have to use a computed observable to have 2, but I can then only get the contents out as a regular array by doing b.get() and so I don't have 1.
I would maybe expect calling something like .filter(...) on an observable array would return a read-only observable array whose contents contain everything in the original array (subject to changes) that passes the filter function. So adding or deleting an element in the original array is propagated to the filtered array. Is this something already possible or is it worth implementing using array.observe?
The text was updated successfully, but these errors were encountered: