Fork me on GitHub
#fulcro
<
2019-04-15
>
tjh.xbow03:04:23

what is the latest version for Fulcro Inspect ? 1.0.11 ?

tjh.xbow03:04:58

I can't check chrome store from Mainland China

claudiu05:04:37

@UG7NHNTE3 Yep, the latest version from is

Version
1.0.11
Updated
April 12, 2019

danielstockton12:04:31

I've been wondering the best way to model something in om.next and I guess it probably applies to fulcro too. I have components that are selectable and can appear in multiple levels in the dom tree. What I'm trying to work out is the best way of storing the selected element on state, and the best way of propagating it to all the selectable components (they need to update to indicate which one is now selected on click).

danielstockton12:04:42

It works to query for the selected element in the root component and pass this down through the entire tree. I'm not sure if there is any other way of doing this that involves the indexer, without having to re-render the entire tree?

danielstockton12:04:23

It's slow and i also have problems transacting from components deep in the tree, because they or their parents don't have queries

wilkerlucio13:04:05

@danielstockton you can use the same ident on the components that share data, that's the way to go on both om.next and fulcro, so when you update on the DB everybody will be looking at the same place, updating then all, a :ui/selected? key can do the job

danielstockton13:04:52

Ok but what would the query look like on these components? Don't I have to workaround the 'everything must compose from root' rule?

danielstockton13:04:24

Can i just stick a query on these components that doesn't necessarily compose back?

wilkerlucio13:04:43

@danielstockton you need to make references as you go, the query would be simple, juts [:some-entity/id :ui/selected?], just do that for every component, using the same ident so they point to the same place, if you are rendering it multiple times on the screen I expect it to already have some way to referencing the same point, there is no generic answer since that heavily depends on how your UI is structured

danielstockton13:04:46

Ok, i'll try something along those lines. Thanks

tony.kay14:04:58

@danielstockton Let me chime in a moment. Is this really an Om Next questions (i.e. is that what you’re using)?

danielstockton14:04:18

Yes @tony.kay, om.next is what I'm using atm. Is fulcro considerably different in how i'd model this?

tony.kay14:04:32

“it depends” 😉

tony.kay14:04:33

so Om Next has the parser…so you can model any attribute in the tree via some invented keyword and then write logic in the parser to satisfy that query. Did I understand you right that you’re modelling something like a radio button situation?

tony.kay14:04:19

and since the parser can use parameterized queries, you have power there to “make up” some additional meaning for queries.

tony.kay14:04:05

Fulcro has the opinion that while this is a more powerful model in theory, it does not scale well in practice, so it leverages a more data declarative approach: your state is what matters, and specialized logic is more easily understood in mutations and local UI logic.

tony.kay14:04:40

so yes, I might model a particular problem quite differently between the two, even though they share some central concepts and query language.

tony.kay14:04:51

In Om Next I have no guarantee, as a reader (of the UI code), of what a query means…the parser can change contexts as it goes and do pretty much anything…but the more of that you do that the more confusing everything becomes, so I doubt anyone would actually do that in practice. In Fulcro you can just “follow the data”. The query’s primary usefulness, IMO, is for the full-stack story: How to I load/normalize/insert a tree of data into my local state, and how do I get back out what this particular bit of UI needs?

tony.kay14:04:39

So, back to your original question: are you trying to model the basic idea of a radio button? One thing selected causes others to unselect?

danielstockton14:04:25

Yes, that's exactly what i'm doing

danielstockton14:04:31

I understand what you mean regarding om next and the parser allowing pretty much anything which can become quite hairy. Less familiar with how it would be done in fulcro.

danielstockton14:04:27

And the queries usefulness is being able to grab all the state you need in a single query, as opposed to lots of other options like reagent/rum/re-frame which let you grab state here and there however you please.

tony.kay14:04:28

OK, so the first thing to realize is that there is one fact of interest in a radio button situation: which thing is selected….you would never ever model that with :ui/selected because that “denormalizes” that fact.

danielstockton14:04:07

Yes, that's actually how im doing it now. I story an id of the selected item, and in each item i check if its selected...

tony.kay14:04:19

I’d make an immediate parent the owner of that fact

danielstockton14:04:21

The only problem is transacting when there is no query deep in the tree, and efficiency of re-rendering the entire tree

danielstockton14:04:51

Right, why the parent and not the element itself?

tony.kay14:04:58

Om Next “efficiency” is up to you making sure the component in question has a query and composes to root…no compromise there

tony.kay14:04:03

if the component also has an ident and you turn on the optimization for it, then it can run the query from that component, otherwise the query always runs from root…this is a necessity in order to get the flexibility of the parser…it cannot default to “on” because your parser needs to gaurantee it can do ti.

danielstockton14:04:26

Yeah, but the way i did it was query in root and passing it down all the way through the tree... i think i can solve that with the 'ident loop' idea

danielstockton14:04:58

Turn on, you mean :root-query true on the AST? (remembering something of the kind)

tony.kay14:04:59

nope…the ident won’t work in Om Next if the query doesn’t go to the component

danielstockton14:04:17

Yeah, i'd have each component include that same ident, and include the query from the child

tony.kay14:04:31

but they aren’t the same thing

tony.kay14:04:56

an ident is the identity of something…it’s like a Datomic Lookup Ref

tony.kay14:04:05

it gets you to an entity in your database

danielstockton14:04:58

But could something like [:page/els :selected] not be the identity of the currently selected thing?

tony.kay14:04:04

efficiency of rendering the tree has two components in these libraries: 1. Can I get the data for the component in question without running the query for everything 2. Can I avoid doing the work of VDOM on side branches? (1) only is possible if the query is there (2) Is only possible if you make the components “true components” (e.g. defui shouldComponentUpdate returns false on the side branches)

tony.kay14:04:56

So, I’m going to back off of Om Next for the moment…take the parser out of the equation. I think it makes the whole thing very difficult to talk about because you’ve got “too much rope”.

danielstockton14:04:17

(om/get-query Child) all the way back to root, pulling that ident up the tree

danielstockton14:04:32

2) is definitely true of any react wrapper

danielstockton14:04:09

i.e. don't make your render too big, compose it of components that can easily have their props compared

tony.kay14:04:22

Here’s how to model it in Fulcro:

(defsc Car [this {:car/keys [id name]} {:keys [selected onSelect]}]
 {:query [:car/id :car/name]
  :ident [:car/id :car/id]}
(div 
  (input {:type "checkbox" :checked (= id selected)  :onClick #(onSelect id)])})
  ...))

(defsc CarList [this {:car-list/keys [cars]}]
 {:query [:car-list/selected-car {:car-list/cars (prim/get-query Car)}]
  :ident [:car-list/id :car-list/id]}
  (map (ui-car #(prim/computed % {:selected selected-car :onSelect #(prim/set-value! this :car-list/selected-car %})) cars))

tony.kay14:04:54

Any stateful component in Fulcro and Om Next will compose queries to root…but that doesn’t mean you think about it any more than one level at a time.

tony.kay14:04:06

Om Next makes it a pain to initialize that starting state…Fulcro does not

tony.kay14:04:34

so, the temptation to “keep the query close to root” in Om Next is simply an indicator of the pain I’m talking about that led me to make Untangled/Fulcro.

tony.kay14:04:26

I have a clear way of loosening the requirement of “compose to root” (slated for Fulcro 3), but to be honest we’re debating if it would actually be an improvement, because it turns out that a composed query gives you some interesting novel power.

tony.kay14:04:17

but in both libraries, for now, you compose the queries to root

danielstockton14:04:35

Right, so fulcro somewhat automates the query composition?

tony.kay14:04:10

Fulcro just has you compose initial state alongside the query in a tree, auto-detects it on startup, and normalizes that using the query

tony.kay14:04:26

You can do it in Om Next very easily the same way…it just isn’t written for you

tony.kay14:04:11

and Fulcro gives you a clearer way of doing all of the loading for the components that are not part of your initial state….Om Next has you embed all of that mess in a parser

danielstockton14:04:20

Might be missing something. I don't see in the above example what links the two queries

tony.kay14:04:34

oh crap, that’s because I didn’t write it right 😉

tony.kay14:04:05

limited time, so was typing fast 🙂

tony.kay14:04:23

and CarList would compose to it’s parent…but CarList doesn’t need to know anything about that

tony.kay14:04:36

Neither does Car…it just allows “control of selection” from the parent

tony.kay14:04:53

the fact that the query is composed is the concern of exactly one parent

tony.kay14:04:29

(at any given level)…which is why it isn’t a big deal IMO…esp if you have initial state to set up the state-based ident linkages at start time

tony.kay14:04:22

What Wilker was getting at is that two different “views” of a given thing (e.g. CarForm, CarListItem, Car) all should have the same ident, and therefore share the same place in state….they are diff views of the same normalized data.

tony.kay14:04:33

In a “list of cars” the parent might want to allow you to select 3 of them…in a radio, only one, etc.

danielstockton14:04:39

Ok, yep. Doing it like this, with the parent rather than all the children 'knowing' who is selected; would it not be less efficient than having that in the child's query and triggering a re-read of that ident?

tony.kay14:04:54

(though in the former I’d probably put :ui/selected on that component and drop the parent control)

tony.kay14:04:31

you’re prematurely optimizing something that doesn’t matter

tony.kay14:04:39

and making your query more difficult in the process

tony.kay14:04:58

every one of those children has a shouldComponentUpdate that will prevent any real work on a ref compare.

tony.kay14:04:09

1000 children probably takes a microsecond to short-circuit

tony.kay14:04:23

it’s silly to complect your code for that

danielstockton14:04:43

Yep, right. It's then important to do what was said above, and make sure CarList is split into subcomponents and not a really heavy render

tony.kay14:04:43

(though I didn’t write the parent correctly for that to be compltely true)

danielstockton14:04:15

Because you're using computed props? Or what's wrong with what you wrote?

tony.kay14:04:35

yeah, because I’m passing a lambda that will change every time

tony.kay14:04:01

so let’s say that you wanted to query for the “who is selected” fact on each child. Don’t you have to re-render them all in a radio situation?

tony.kay14:04:18

all of them need to compare “am I it?” to the new fact

tony.kay14:04:42

so unless you put the checkboxes on the parent I think you’re screwed, so to speak 🙂

danielstockton14:04:16

Yep, very good point

tony.kay14:04:20

though again, React is fast enough in practice that it usually doesn’t matter

tony.kay14:04:05

real DOM changes are the heavy hitter

tony.kay14:04:50

Fulcro is more about cleaning up the data model than being the fastest thing on the planet…the query processing overhead usually dwarfs the React CPU

tony.kay14:04:00

same in Om Next for that matter

danielstockton14:04:32

Yes, same with persistent immutable data structures

tony.kay14:04:34

but I work on some pretty varied UIs with clients, and it is still fast enough in most cases without much trouble

danielstockton14:04:36

Makes sense, usually that's a sensible trade-off

tony.kay14:04:13

and I know how to make it about 10-20x faster than it currently is 🙂 Just need to find the time

danielstockton14:04:36

I also needed a hover state as well as selected, and for that i used component local state and an atom to hold the last hovered (to unhover on mouseout). Seemed to work pretty well and fast, and I guess for something so fleeting it isn't important to be in the app state

tony.kay14:04:57

(Om Next actually wins on performance at the moment, I removed some of it’s optimizations because they were not recommended beyond React 15)

tony.kay14:04:17

yes, hover = component local state for sure

danielstockton14:04:33

What optimizations were not recommended beyond React 16?

tony.kay14:04:43

specifically it uses forceUpdate

danielstockton14:04:17

Yes, component local state but i also needed them all to be able to know what was last hovered... hence the atom, but outside app state

tony.kay14:04:47

I would not use an atom, I’d use CLS in the parent…but to each his own 🙂

danielstockton14:04:52

Hmm right, to force re-rendering only a component identified by the indexer to have changed?

danielstockton14:04:23

Problem is, they don't all share a common parent, they're nested at different levels in the tree

danielstockton14:04:15

It's a kind of editor built itself out of components, and various elements in the tree are selectable in order to edit them

tony.kay14:04:23

ah, in that case I would have prob made it normalized state 🙂

tony.kay14:04:01

or perhaps just root-level state with a link query

danielstockton14:04:02

As in, made it part of the app db?

tony.kay14:04:48

:query [ [:root/last-hovered '_] :other :props]

tony.kay14:04:05

then that prop lives at the root of the db

danielstockton14:04:06

Would there be disadvantages to just using an atom? It still needs to compose right? Isn't that similar to what we was needed for the selected item?

tony.kay14:04:22

refresh: When you change that atom, how does the UI know in order to refresh?

tony.kay14:04:51

I don’t see selected item as a “global concern” in a general radio button use-case

danielstockton14:04:55

I got a bit creative and all the logic is in mouse-out and mouse-enter events, which triggers updates on each components local state to refresh them if necessary

danielstockton14:04:36

Yeah, it all needs a bit of reorganization based on what we've discussed

tony.kay14:04:52

I gotta go for now. Good luck 🙂

danielstockton14:04:00

Sure, thanks a lot!