I'm getting the sense that fulcro is better approached from a bottom up perspective if I want to modularize stuff.
ok I made an example that sort of captures how I've been thinking about this.
Wouldn't the parent component simply get a list of images and map Image Container over it? Normally you would get data from Pathom instead of hardcoding them in initial state. Not sure where you want the images data to be defined, in the parent component?
Look into the book at the person list example. Isn't it the some, only you query for types of images (ie "dog list" instead of "friends list") and display each via Image Container instead of Person?
I figured the parent component would define the source (or get it from its parent) but I think your right. I was looking at the person example last night and I'm pretty sure I'm just thinking about the design wrong. I'll see if I can't get something that resembles the person example
(defsc ImageContainer [this {:image-container/keys [id src alt] :as props}]
{:query [:image-container/id :image-container/src :imag-container/alt]
:ident :image-container/id
:initial-state (fn [{:keys [id src alt] :as params}]
{:image-container/id id
:image-container/src src
:image-container/alt alt})}
;css/context + add id attr + maybe surround with dom/div or more
(dom/img {:src src :alt alt}
;...
))
I decided to commit to the image example since I'll have to deal with it eventually. Is this the right sort of approach to building components?
If I want to build a container so that all the images have the same shape then this would make sense I think. I could use this in a parent's initial state and pass it the context the parent knows to build the component.
The problem though would be the parent's design constraints.
I can't make an arbitrary number of image containers and making a large number of them in the parent would get excessive, especially in the initial-state
and the parameter list. For example, if the parent is a component that uses, say, four image containers then that's four parameters to add to the header.
I need to read up on the data flow after the initial state. Once the initial state is created, what do the parameters and query do for me?
yeah the more I think about this the weirder it gets. I have to be thinking about this wrong. So If I define a component that uses a bunch of sub components then that just explodes the size of the initial state since I have to define both what the subcomponent gets and all the components of that sub component and all they get
hmm, if I want to deal with that then I could maybe define an instance of a component if I know the data will be mostly or completely static. Very few things on the page actually communicate data so defining instances of compositions of components makes sense.
Computed columns can get complex. There are a number of issues around that which need to be solved, since a computed column might in turn want to deal with data that is naturally nested…for example, you might say “I have three line items on this order, so I’d like to be able to query for the line items in this column, but display a total that is computed”. The options are kind of endless. That said: computed colums *are possible with the current design: Add an attribute to your model with whatever type you want the column to have. It doesn’t matter that the server will respond to it with nothing. Add that column to your report. Set the field-formatter for that column. That function will receive the report instance, the row props, etc. You can make up whatever you want for that column.
So, I think that case you’re pointing out is already supported.
What we need is improved docs 😄
I’m happy to announce the release of Fulcro 3.2.16. This version adds a new plugin to support completely synchronous operation (sans remotes) which can be used to improve regular application performance, but can also be used to enable trivial full-app testing that has no asynchrony even in js. It also removes some internal debugging logic that was unnecessary and affected dev-time performance. See the new chapter in the book at: http://book.fulcrologic.com/#_synchronous_operation https://github.com/fulcrologic/fulcro
Thanks to the Folks at Dataico for funding this cool addition: @pvillegas12 @dansudol
Be sure and check out the cool new testing abilities this adds: http://book.fulcrologic.com/#_testing_full_apps_using_synchronous_operation
BTW, that testing works in CLJC…I’ve gotten in the habit of writing all of my Fulcro code in CLJC (which is only slightly painful in UI…just stub all the low-level js stuff out with #?(:cljs …)
). This lets me run all of my tests in a normal Clojure REPL with a simple kb shortcut, which is very nice since the CLJ runners are much more full-featured.
Just cloned the fulcro-rad datomic-cloud branch from the repo and ran the recommended commands. I’m visiting the http server at 8081 but I’m only seeing a blank, brown page. Is that expected?
[:main] Compiling ...
------ WARNING #1 - -----------------------------------------------------------
Resource: node_modules/semantic-ui-react/dist/commonjs/lib/AutoControlledComponent.js:219:2
JSDoc annotations are not supported on return.
--------------------------------------------------------------------------------
Sounds like an issue in semantic ui itself which is a javascript ui library, should be safe to ignore but if not maybe try bumping the version on it?
It should be port 3000 8081 is the shadow http server but it’s not used in the demo repo
Isn’t that 9630?
I was wrong, you are right
There’s the server and the http dev server
But the Fulcro backend is on 3000
So I just did some performance/behavioral analysis on the differences between the standard tx processing and the new pluggable synchronous version. Of course they both do roughly equivalent work, so the overall CPU usage is roughly equivalent, but here is an example of where your end users would experience differences. The scenario is the full-stack TodoMVC app, where an item is being added to the list. This involves: • An optimistic update to add the item to the list • A network request to add it on the server • A server response (which could result in data changes..tempid remapping, etc.) • A secondary render to update the view based on the server result The old tx processing does everything asynchronously, and schedules animations using requestAnimationFrame. The result looks like this in a flame chart:
Notice the network request at the top. The overall time from where the user presses “Add” to the final network result refresh is about 81ms.
The new synchronous tx processing on the same interaction looks like this:
Notice the network request starts while the initial tx processing is still running, and actually completes before the final DOM refresh of the keypress (7ms), triggering the UI update for the network result. Total time from keypress to completion of final render after network interaction: 15ms. Again, no CPU overhead difference…they both consume the same processing time; however, the new one feels like 60fps with full stack, whereas the old one feels more like 10fps.
So, not only does the new tx processing make for interesting sync testing possibilities, it leads to dramatically improved user perception of “snappiness” in full-stack scenarios.
f-in-a .. its so good .. wow
Nice!