-
-
Notifications
You must be signed in to change notification settings - Fork 116
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
[v2] Facet API proposition #337
Comments
Related to #240. |
@ivan-kleshnin @saulshanabrook @AutoSponge any opinion about this? |
I guess that one of the perks of this method would also be to be able to create out-of-the-tree facets also, if needed. |
Yes, this may be a problem in some occasional cases.
How this matters? If key with dollar is stored in variable it still works.
Here factory just returns data, so we should either discuss it's specific structure (back to the root question of the subject) or specific instance (back to OOP). |
Baobab will, by convention, understand that a |
I remember that. I've read
as containing two reasons, not one. |
@christianalfoni any insight on this after your experience with |
I'll show you something (albeit highly edited) that I used in a recent app and why we liked it: //from the file appModel.js
import Baobab from 'baobab';
//facets using constants are based on simple cursors with an array path,
import * as cursor from 'app/constants/path-constants';
//facets with string names are based on other facets
import * as facet from 'app/constants/name-constants';
export default new Baobab({}, {
autoCommit: true,
asynchronous: false,
synchwrite: true,
facets: {
// SIMPLE Facets
currentEntryIndex$: {
cursors: {
data: cursor.CURRENT_ENTRY_INDEX$
},
get: utils.getData
},
entries$: {
cursors: {
data: cursor.ENTRIES$
},
get: utils.getData
},
user$: {
cursors: {
data: cursor.USER$
},
get: utils.getData
},
//COMPLEX FACETS
entry$: {
facets: {
currentEntryIndex: facet.CURRENT_ENTRY_INDEX$,
entries: facet.ENTRIES$
},
get: ({entries, currentEntryIndex}) => pluck(currentEntryIndex, entries)
}
}); This code was very easy to follow, extend, and create conventions around. It supplied all of the data for the app's components. Most components don't care if they're using a Using the same const {entry$, user$} = appModel.facets; Then all updates were passed from the appModel to the view model: //this helper function just uses the identity fn as a transformer
updateFromCursor('user', user$); Conventions: If someone had a problem with their component, it was easy to see the view's state and track down the flow that created that state. From above, Conventions help when things get complicated: In this (seemingly) simple game, one facet merges 8 other facets and the view model that uses it has another 10 facets it needs to render correctly. It then holds another dozen or so models that only pertain to the view (it's not persisted back to the app model). There's only a single cursor in that view. Therefore, it can only update 1 thing in the app model. You can find what it has access to update and where it happens in seconds--despite the complexity of the view's logic and many handlers. @jacomyal 's code looks very similar to what we did for our game (which is now live in prod). Our only difference is that we wanted a clear separation of app model and view model references so we added Again, I'm of the opinion that most everything works as intended. I wouldn't want to have key names forced upon me. |
Just putting in my thoughts :-) The reason a It also opens up for doing other things, like passing in values to that function, if needed for some reason. Just an imaginative example: {
myMapping: function (mappings) {
return mappings.once({
...
});
}
} It also makes you able to define the "facet" without a dependency to the library itself, just use plain javascript. Not that important, just less dependencies. |
Hello @christianalfoni. I am currently converting v2 to use an equivalent of @jacomyal's proposition (instead of facets we'll have monkeys or dynamicNodes for the less fancy ones). I get your point about using functions instead of a library utility but I guess you can create your dynamic nodes as you wish and then feed them to the constructor without further ado anyway. I like the idea of passing something to the function however. This could prove useful in the future. Could make a solution for the relative paths some people want to see in the library. Anyway, the idea here is mostly to drop this sigil-like |
I think passing values to facets would be very useful, like @christianalfoni proposed. Sure you can create facet's but that seems more cumbersome and verbose than allowing input directly to the facets. |
Could you precise what you mean @saulshanabrook? What could we pass to the facets exactly? |
Also @christianalfoni, the idea to free ourselves from the |
Like for example, I have a lot of In the current syntax, maybe this could be supplied by passing an object after the facet, when getting it. Like |
Ok, I see. What you'd want is a way to pass arbitrary data to a facet somehow. However the last line you wrote would not work in v2's current installment because it means you want to get something beneath the return value of your facet. Something along the way of "I need to get the first element matching I guess this is basic function-land here since this would mean that the return value of a node of the tree would be indeterminate and this is not possible. We would then have something like a "quantic" node whose value could be a lot of different things. I guess facets etc. shouldn't be something else than a reduction of the current state. |
Any opinion about this @christianalfoni @jacomyal? |
@saulshanabrook, I guess you could solve your problem by creating a facet returning a kind of index of valid items in your collection then using a selection pointing beneath the facet. Something like: |
@Yomguithereal Yeah that would work. |
Haha, I am loosing my mental image here :-) Yes, I agree that facets are just a reduction of current state... you know... what would be interesting is to remove dependencies. Make it self aware. {
myStateValue: function (value = [], get) {
return value.map((id) => get('projects', projects[id]));
}
} Okay, hang in there, just exploring :-)
You might make an argument on conditional use of Does this make any sense? The thing is that I would love to shorten the syntax of this stuff, just "do it", do not have to make sure that it listens to the right paths etc. It just works :) |
This perfectly makes sense @christianalfoni. However I think we are pouring a lot of magic here. I fear it might get very costly and make the system work in some unexpected ways some times. |
Yeah, that is certainly a good point! I think I will make a branch on immutable-store, just to try it out. A fun project. I will let you know how it works out :-) Currently the mapping functions in immutable-store, which are very similar to facets works very well. That being a But yeah, I do not see any issue allowing both though. |
I agree with you concerning the computed state within the tree. While having the computed state outside the tree has its advantages, it forces the components to "know" from which part of the state, static or dynamic, they should draw their deps and this is tiresome. This is even more tiresome when there are two different APIs to access somewhat similar things. |
By the way, @christianalfoni, I just released |
Let me know if you start experimenting on |
Ah, cool, hehe... monkeys... nice :-) Thought anymore on how to import state? You mentioned Yup, will let you know! |
I often find myself merging the state as stored in the |
But how does that work if a monkey is nested? Would not a merge at top level override the monkeys? |
I use a deep merge. Like the lodash's one for instance. |
Ah, I see, so you deep merge the state object before you put it into the tree? And if you want to do an import you simply grab the state object, deep merge and do a new "set" on the whole tree? |
Hey all and @Yomguithereal, sorry to interrupt the flow of conversation. I updated my my app to use the latest version of the baobab v2 branch and am now hitting errors due to the api changes discussed above, or at least I think that's what is causing it. I thought I'd double check with you as to what the new facet syntax is. Here's the one version of the old syntax:
Is the new syntax something like this?
Thanks! Also, moving away from the $ seems like a good move, be it to a pure function or a library method. |
@christianalfoni that's it. I shall add soon a @tnrich, the only changed thing is indeed the computed data syntax. You have it right except that it's not |
@Yomguithereal There seems to have been done great work here: https://www.npmjs.com/package/deepmerge. I am going to use that for the new |
Lodash's merge or even jQuery's extend also work that way. On |
Ah, thanks for that, will use that one instead then :) |
If you use it, please remember that my version is variadic and will mutate the first given object (like lodash). So you'll have to pass |
Okay, cool, thanks for the heads up! |
By the way, @christianalfoni, with cerebral, do you update with requestAnimationFrame or setTimeout 0? I am pondering this for #333. |
I use That said, if there is too much going on in regards of state change logic, it would make sense to trigger a new I would always use But I also give the opportunity to use |
And if the UI library already has At least, that is the theory! hehe |
I have been discussing the new facets API with @Yomguithereal, and here are the problems with the current propositions:
$
(mostly when it comes from JSON documents).Another idea would be to use a facets factory. This would give us something like:
What do you think about this?
The text was updated successfully, but these errors were encountered: