Fork me on GitHub
#fulcro
<
2018-06-29
>
tony.kay02:06:48

Fulcro 2.5.11 on Clojars. This release cleans up networking just a bit. I found some minor issues while creating this file upload demo: https://github.com/awkay/file-upload-demo Also Fulcro Inspect had a few little issues around networking as well, but those are fixed in Inspect beta8 (which is currently a snapshot).

tony.kay04:06:27

I also just updated the book some with respect to middleware docs: http://book.fulcrologic.com/#_client_response_middleware

šŸ™‚ 4
myguidingstar04:06:58

@tony.kay do Follow-on Reads (in prim/transact!) trigger ui refresh if the component is the reconciler?

tony.kay04:06:31

if you transact on the reconciler, you get a root refresh

myguidingstar04:06:43

does that mean follow-on reads are obsolete there?

tony.kay04:06:51

no, not obsolete

tony.kay04:06:54

follow-on reads never meant anything when reconciler is used with transact

claudiu05:06:59

@tony.kay in the patreon post you mentioned shadow-cljs and mount. Is mount the lib you preffere between component/integrant/mount ?

claudiu05:06:50

Still trying to pick one of these :))

tony.kay05:06:21

Iā€™ve recently started using mount and really like it. Cleans up things quite a bit, actuallyā€¦a lot less OO

tony.kay05:06:20

integrant uses component I think

tony.kay05:06:21

that little bit of code is most of ā€œeasy serverā€, and integrating other ā€œthingsā€ with it is as simple as throwing them into the env in the wrap-api

tony.kay05:06:35

no need for parser injections and other such backflips

kardan05:06:48

The Duct framework used component but now uses Integrant. On my side I really like Integrant.

tony.kay05:06:41

so is Integrant using component internally?

tony.kay05:06:08

ohā€¦I seeā€¦I was looking at the wrong docs

tony.kay05:06:22

after looking over the docs, I still think I like mount better personallyā€¦part of what I really like about it is that you donā€™t have additional config to manage. In component youā€™ve got to ā€œbuild the systemā€ out of OO components. In Integrant your ā€œconfigā€ isnā€™t even easily coupled to the code that does the actions (defmethods are not well supported by nav in IDE/editors). In mount, you just defstate things and require them. Itā€™s eliminates tons of cruft, but still gives you the power to ā€œsub outā€ parts of the system. More importantly: navigating the code is natural and supported by the IDEā€¦want to know where that ā€œconfigā€ thing is coming from? Hitting ā€œjump to definitionā€ in the editor takes you right to the defstate. The biggest possible downside is: if you require it, you get it. So, stale requires in a namespace can accidentally start up something you didnā€™t need.

tony.kay05:06:55

My current library recommends when building a new business software project are pathom, mount, promenade, clara rules, datomock (if using Datomic). Iā€™m playing with immutant web server instead of httpkit, and it seems good so far, but nothing has stood out about it for what Iā€™ve done to date (other than the benchmarks are better).

sundarj13:06:02

i'm building a personal fulcro app; i know i want to use datomic/datascript for the database, and i've been considering using clara for the logic, but it's less clear to me how using that would work in the context of fulcro (as opposed to e.g. precept). how do you approach it?

claudiu14:06:12

@U61HA86AG the client fulcro has its default parser. No idea how easy it would be to have the state in datascript. Think this is what you're looking for

sundarj14:06:44

@U3LP7DWPR you can run datascript on the server as well, that's what i was referring to. i think the default db format works well on the client.

tony.kay15:06:05

I had not looked at precept. Interesting approach.

sundarj15:06:47

i haven't given it a try as of yet, but yeah. it's definitely intriguing

tony.kay15:06:03

So my first thoughts are that I wouldnā€™t want a truth management system running the UIā€¦because the UI itself is a very contextual thing, so it seems that the rules would get unwieldy. But this is my first exposure, so I want to let it ā€œbakeā€ in my head for a while and read their examples.

sundarj15:06:18

fair enough

OliverM15:06:42

Interesting - reminds me strongly of Arachne's FactUI: https://github.com/arachne-framework/factui

tony.kay15:06:39

factui credits precept

OliverM15:06:56

that explains it!

tony.kay15:06:50

The short answer that comes to mind, @U61HA86AG, is that you could replace the client database and parserā€¦the parser query-side would query for ā€œfactsā€ in Clara, and the mutations would insert/retract facts. Not sure how it would perform, but it would give you a clear decouplingā€¦that said, youā€™d be back to Om Next since youā€™d then have to figure out how to do the data-driven networking story and graph queries.

tony.kay15:06:32

My current use of Clara is for more focused problem solvingā€¦e.g. shopping cart business rules. Insert some items in the cart, run rules, then run queries about things like shipping charges and discounts. Then persist those ā€œresultsā€ to the UI database. It isnā€™t ā€œautomatic UI updateā€ in that sense, but it gives me a more functional interface to using rules to replace hand-coded logic. I see Clara as a big function I can call to do complex logic and return a result (rather than side-effecting in the rules themselves, which irks me)

tony.kay15:06:31

Youā€™ve got this beautiful rules engine that is completely immutable, and yet everyone still wants to side-effect external changes. Itā€™s convenient, but truth-management is hard to tell how to ā€œundoā€ side effects.

tony.kay15:06:16

So, what these UI frameworks are doing is tying the UI to facts in the database by you defining queries that feed the UIā€¦so inserts/retracts from the rules update the UI

tony.kay15:06:24

thatā€™s pretty coolā€¦Iā€™m still thinking about how that functions with the ā€œrest of the storyā€ that you needā€¦like server integration.

tony.kay15:06:14

I like the ideaā€¦hooking facts directly to UI.

sundarj15:06:51

i really like Fulcro's client-side and networking story, but i feel like the server-side is more open-ended (with good reason). that clarifies things re: how you use Clara. interesting! i think facts and rules engines are a really cool idea, so am always curious about how they're used.

tony.kay15:06:36

Thereā€™s almost certainly an integration story here where Clara could maintain facts, Pathom could run graph queries against the Clara queries

tony.kay15:06:02

thanks for askingā€¦.Iā€™ll be munging this in my head for a bit šŸ™‚

sundarj15:06:37

haha, thanks for the in-depth response!

tony.kay15:06:37

might be interesting to make ā€œderivedā€ data be Claraā€¦e.g. put in a parser that uses the client database for hard data, and let certain keywords trigger queries for clara facts. Then mutations would insert changes into the client db, and add/retract relevant facts to claraā€¦refresh of UI would then automatically update derived data from claraā€¦all sorts of possibilities.

sundarj15:06:47

i've now got a better sense of how to direct my hammock-time šŸ™‚

tony.kay05:06:13

Iā€™m sure my list will expand as I find further holes. The promenade library has a lot of alternatives, but I liked his approach to a monad-like error handling. I think the library could probably use a bit more, but itā€™s a nice clean start.

kardan05:06:34

I have not used Mount at all, and I didnā€™t mean to push you in the Integrant camp. I used Duct at work hence itā€™s ā€œcloserā€ for me.

myguidingstar18:06:59

another Duct fan here šŸ˜„

wilkerlucio12:06:33

@tony.kay there is a root render on the transact on reconciler, even if the user send an ident too?

tony.kay16:06:59

@wilkerlucio good questionā€¦I donā€™t remember on that case

tony.kay16:06:18

so, reviewing the render code I may be misrepresenting what using the reconciler does.

tony.kay16:06:28

when it is use as transact source

tony.kay16:06:41

transact itself queues the ident of the component, OR the explicit ref (if given), and any declared refreshes on the mutations that ran. So @myguidingstar I think I misled you. I was thinking about swap! on app state, not transact on reconciler

tony.kay16:06:20

It also, of course, queues any keywords that are in the transaction

tony.kay16:06:56

transact is always a targeted updateā€¦I obviously was not awake enough when I answered that question

tony.kay16:06:11

it auto-queues the component if it has an ident (which the reconciler does not). Using the additional ref arg (as Wilker points out) is a substitute when using the reconciler and you want :ref to be in the mutation and refresh list.

šŸ‘ 4
tony.kay16:06:00

I thought there was a code path that said ā€œif nothing is queued, then root refreshā€, but Iā€™m not seeing that as I look through the codeā€¦so a transact on reconciler with no ref or follow-on reads would refresh nothing. I need to test that, because that is not an intended behavior

wilkerlucio16:06:56

@tony.kay to me the current behavior was my expectation, I'm afraid if we don't send nothing we might get unintended heavy refreshes, if this is the state I would prefer it stays that way since it's already that and for "easy refresh" we have the keyframe mode, so I rather keep the "advanced" mode to be more performance driven than easy driven

tony.kay16:06:16

So I would disagree: If you use the reconciler and queue nothing for refresh with follow-on reads or a ref, then youā€™ve clearly changed the app state and a UI refresh should occur. In the absence of any way to choose what to refresh, a root render should happen.

tony.kay16:06:08

99% of mutations will be against components or have follow-on reads. Optimization is opt-in by specifying idents (which you want anyhow).

wilkerlucio16:06:11

not nescessarily, you could be swaping something that's not currently visible, that would not need any UI refresh, in this case the root render will be a waste

tony.kay16:06:38

but no props will change on the query result, so nothing will actually happen at the react layer

wilkerlucio16:06:03

still overhead, I wonder how much, I think it's worth measuring

wilkerlucio16:06:21

ah, it would stop at the root? or it will call everything?

wilkerlucio16:06:57

it's like force root render (which will force everything down to refresh) or it would do a normal root render which can stop at the root element?

tony.kay16:06:26

So: 1. Your query+refresh should be tuned (e.g. via unions) to run in 10-30ms. In practice this is normally easy to achieve. 2. shouldCompontentUpdate stops tree traversal on no data change. A ā€œrootā€ render would short-circuit the entire React system on no-visible-changes The only case youā€™d really want to avoid root render in this scenario is if youā€™re doing frequent updates (e.g. many times a second via reconciler) of non-visible data to avoid the query CPU overhead

tony.kay16:06:39

yeah, I didnā€™t say ā€œforce rootā€

tony.kay16:06:50

just the normal (optimized) render from root

tony.kay16:06:56

instead of component-targeted render

wilkerlucio16:06:13

yeah, in this case I think it's ok, I was with the impression it would be a force root render

tony.kay16:06:24

nopeā€¦not trying to circumvent optimizationsā€¦

šŸ‘ 4
currentoor17:06:21

@tony.kay i see last night you shared your current library recommends when building a new business software project, so you don't use specter anymore? or is that just not a critical part of your stack?

tony.kay18:06:20

oh, I like specterā€¦I think it is a great library, tooā€¦just havenā€™t edned up using it as much in practice

tony.kay18:06:37

partly that is my lack of time to learn it btter when there are solutions that ā€œworkā€ that I alraedy know šŸ™‚

tony.kay18:06:53

Thanks for reminding me of itā€¦I keep meaning to get more practice with it, because I do, in fact, think it is a super-nice library for general-purpose work

wilkerlucio18:06:04

I really like the specter ideas too, but I find myself ending up just using the clojure basic things, they solve 90%+ of the cases, so I end up not using specter

wilkerlucio18:06:20

(I also don't like that he decided to use uppercase for a lot of things there, looks ugly...)

tony.kay18:06:45

In practical terms thatā€™s how Iā€™ve been as well; however, whenever I look at the library I think ā€œI really need to learn thisā€¦it would make so many things less verboseā€. Uppercase is less desirable, but where that is slightly ugly, the sheer concision is a big boon. Iā€™m adding it back to my list of things I should really spend a day learning well

tony.kay18:06:07

the other issue that I worry about is that if you write a bunch of stuff with it, it might make contributors on OSS harder to find because they donā€™t ā€œgetā€ your codeā€¦but I think that is wrong-headed

tony.kay18:06:23

ppl are willing to learn arbitrary libraries as long as theyā€™re good

tony.kay18:06:01

I think the uppercase is actually a good decision, since it allows you to refer all without stomping on a bunch of accidental collisions.

myguidingstar18:06:16

specter can be very useful when dealing with tasks like garbage collection

wilkerlucio19:06:21

maybe, but I dislike the :refer :all in general, I see it as a bad practice (with a few exceptions, like for testing primitives I found it ok)

currentoor19:06:14

i like :refer :all in some cases, limited to namespaces that have a very specific purpose, like testing files

currentoor19:06:58

i used specter for dealing with the salesforce API, i first wrote a few components in vanilla clojure then converted them to specter, specter was clearly better suited for that

tony.kay19:06:15

and you can always prefix

šŸ‘ 4
currentoor19:06:56

i've been writing business apps for almost a decade but i've never used a rules engine šŸ˜…

currentoor19:06:01

should i be ashamed?

tony.kay19:06:10

(as I actually agree with you @wilkerlucio, I never use refer all šŸ˜œ )

šŸ‘ 4
tony.kay19:06:42

@currentoor nopeā€¦mostly havenā€™t either

currentoor19:06:53

does using something clara really help in this domain?

currentoor19:06:07

and rules engines in general

tony.kay19:06:29

I find most rules engines way too heavy, but I also love some of the benefits

tony.kay19:06:09

Declarative logic is just handy, especially if it is a complex set of intertwined rules

tony.kay19:06:59

and sicne the rules can look more like the problem domain, it is an easier translation from stakeholder to code

myguidingstar19:06:50

I just wonder if anybody is using core.logic these days šŸ˜„

wilkerlucio19:06:55

Clara Rules is something I would love to experiment some time, seems interesting, but I dont have the feel on what they look like in real case scenarios

currentoor19:06:27

yeah same here, i'm just worried about adding it to a core part of my stack then regretting it

tony.kay19:06:51

state machines are yet another one that are super handy in certain circumstances

currentoor19:06:54

so i decided to only use the CSS part of semantic ui and removed all the js factories

currentoor19:06:19

everything was straightforward except the modals, i wanted to get a second opinion on that

currentoor19:06:26

(defsc Modal [this {:keys [show?]} {:keys [header content actions]}]
  {:query         [:show?]
   :ident         (fn [] [:modal :singleton])
   :initial-state {:show? false}}
  (if show?
    (div :.ui.page.modals.dimmer.transition.visible.active
      {:ref (fn [el] (if-let [s (and el (aget el "style"))]
                       (.setProperty s "display" "flex" "important")))}
      (div :.ui.modal.transition.visible.active
        (when header (div :.header header))
        (when content (div :.content content))
        (when actions
          (div :.actions
            actions))))
    (div {})))

(def modal-factory (prim/factory Modal))
(def ui-modal (fn [props ui-opts]
                (modal-factory (prim/computed props ui-opts))))

(m/defmutation set-modal-state [{:keys [show-val]}]
  (action [{:keys [state]}]
    (swap! state assoc-in [:modal :singleton :show?] show-val)))

(defn show-modal! [comp]
  (.add js/document.body.classList "dimmable")
  (.add js/document.body.classList "dimmed")
  (prim/transact! comp `[(set-modal-state {:show-val true})]))

(defn hide-modal! [comp]
  (.remove js/document.body.classList "dimmable")
  (.remove js/document.body.classList "dimmed")
  (prim/transact! comp `[(set-modal-state {:show-val false})]))


(defsc CompUsingModal [this {:keys [modal]}]
  {:ident         (fn [] [:packages-table :singleton])
   :initial-state {:modal (prim/get-initial-state Modal {})}
   :query         [{:modal (prim/get-query Modal)}]}
  (div
    (dom/button {:onClick #(show-modal! this)}
      "show modal")

    (ui-modal
     modal
     {:header  "foo"
      :content "bar"
      :actions (div :.ui.button {:onClick #(hide-modal! this)}
                 "close")})))

currentoor19:06:15

semantic ui requires !important but react inline styles don't support it, so i have to use a :ref

currentoor19:06:07

semantic ui does modals by appending modal elements to the bottom of the body

currentoor19:06:30

but i'm able to get it to work "inline"

currentoor19:06:35

is that an ok thing to do?

currentoor19:06:32

another approach i suppose is to render the modal component at the bottom of my root and use app state to coordinate what goes inside there, seems like that's how semantic ui does it under the hood, but then i loose contextual reasoning

myguidingstar19:06:47

is there a way to tell a mutation to do something with new ids (aka resolved values of tempids from server response)? Listening to all transactions seems overkill. I want to set html5 route to a new url containing the new id after user hits the Save button

currentoor19:06:09

can you do it with a post-mutation?

currentoor19:06:19

in the past i did something like that with a react lifecycle callback, componentWillRecieveProps or something like that, but there's probably a more elegant way

myguidingstar19:06:43

no, I can't. Post-mutation requires you to know its params in advance, not waiting till server response

currentoor19:06:46

Well then the react callback is always an option right? Just update the html5 route in there if the old pros have a tempid and the new props have a real id

currentoor19:06:54

Assuming the components that depends on this tempid is rendered while the url has the tempid/real-id

myguidingstar19:06:43

sure, it's an option though I prefer things go thru fulcro's transaction system

currentoor19:06:24

yeah i see what you mean

claudiu07:06:55

@myguidingstar maybe the new ptransact! ?

Daniel Hines20:06:34

Is anyone planning on submitting anything related to Fulcro/Pathom for Clojure/Conj this year?

šŸ‘€ 12
souenzzo21:06:23

I think that fulcro/pathom/walker are the 3 keyparts to my next stack

tony.kay00:06:30

Thatā€™s a great question. I think the most interesting talk would be if we could get @wilkerlucio to submit a pathom/graphql/fulcro kind of talk

wilkerlucio13:06:23

surely would be fun, not sure what, but I'll probably try sending a proposal šŸ™‚

tony.kay15:06:32

The talk you did in Europe is pretty close I think

tony.kay15:06:01

maybe pointed just a bit more towards a full-stack Fulcro app with GraphQLā€¦but I think the first 1/2 to 2/3rds could be very similar.

Daniel Hines12:07:31

Very cool guys. I'm always interested in hearing what you smart folks have to say.