This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-06-19
Channels
- # aws-lambda (1)
- # beginners (35)
- # cider (7)
- # cljsjs (2)
- # clojure (48)
- # clojure-austria (1)
- # clojure-conj (9)
- # clojure-dev (8)
- # clojure-india (6)
- # clojure-italy (12)
- # clojure-nl (8)
- # clojure-norway (3)
- # clojure-spec (9)
- # clojure-uk (92)
- # clojurescript (103)
- # community-development (7)
- # cursive (15)
- # datomic (75)
- # devcards (3)
- # emacs (3)
- # events (1)
- # fulcro (129)
- # hoplon (4)
- # immutant (2)
- # jobs (10)
- # leiningen (9)
- # off-topic (4)
- # onyx (2)
- # re-frame (45)
- # reagent (39)
- # reitit (40)
- # remote-jobs (4)
- # ring (2)
- # ring-swagger (9)
- # shadow-cljs (17)
- # tools-deps (31)
@tony.kay Seems to work fine so far!
Do load
post-mutations always wait until the load completes before running? In the following I’m getting the console log statement before the load completes…
(df/load this :login/user shared/StaffData
{:params {:email email :password password}
:post-mutation `login-complete
:refresh [:login/user]})
(m/defmutation login-complete [_]
(action [{:keys [state] :as env}]
(.log js/console "Completing login...")
(let [user-name (-> @state (get :login/user) second)
user (get-in @state [:staff/by-name user-name])]
(when user (r/route-to-impl! env {:handler :PAGE/app})))))
(the load is part of a larger component on-click handler)@oliver.mooney They should. Tried on 2.5.7 and the latest 2.5.10-snapshot I'm getting the console.log after the load finishes (added a 5 second sleep to thread)
there is this in the docs "You side-effect. Your mutation will be called at least two times so this is a bad idea. Side effects should be wrapped in the action." but what you pasted seems right
@claudiu thanks for checking. I’m on 2.5.7. My console log from a fresh reload looks like:
Installing Fulcro Inspect
Turning logging to :all (in adaptdb.development-preload)
Installing CLJS DevTools 0.9.10 and enabling features :formatters :hints :async
browser.cljs:27 shadow-cljs: WebSocket connected!
browser.cljs:27 shadow-cljs: REPL init successful
root.cljc:21 Completing login...
goog.net.xhrio.js:627 XHR finished loading: POST "".
It is run after response. How do you know the load isn’t complete when you’re seeing logging?
@tony.kay ah so processing of the load happens in parallel with the post-mutation? what’s the best way to wait until the load is complete in that case?
post mutation is meant ONLY for morphing just-loaded data…it is the reason it exists. period. 🙂
@tony.kay hmmm I’m not getting it. I’m modeling this post-mutation on the example given in the fulcro template app here: https://github.com/fulcrologic/fulcro-template/blob/master/src/main/fulcro_template/api/mutations.cljs
That login-complete mutation has routing on successful login via the LoginPage component here: https://github.com/fulcrologic/fulcro-template/blob/master/src/main/fulcro_template/ui/login.cljc
post mutation is called after the data is loaded, since it's build so that you can move the newly loaded data to other places in your app state. If it were to load before, then you would not have access to what you loaded from the server.
really strange that you're getting that console.log before. Can you try to log user
, maybe it's being called from somewhere else or something 🙂 ?
@oliver.mooney how are you doing networking? I.e. how are you getting log messages out of the network?
@tony.kay only using networking provided as part of the fulcro framework, nothing custom
My handler reads
(defquery-root :login/user
"Supply current user details (logging the user in if not already logged in). Ported from fulcro-template example."
(value [{:keys [db request]} {:keys [email password]}]
(if (and email password) ;; credentials presented; attempt login
(validate-user db request email password)
(let [current-user (-> request :session :staff)]
(timbre/info "Currently logged-in user: " current-user)
current-user))))
So, do this: Log app state from within your client mutation to see if the loaded data is there…console messages can also appear out of order due to async nature of XhrIO
If you’re using the stock stuff, then post mutations will run after load is complete
The state is there with the expected data from the load! Good point about the console messages appearing out of order, I should have thought of that. But now I don’t know why my post-mutation routing isn’t firing. If I press the login button twice (ie. so the second time the data has been loaded and normalised) the post-mutation works and the screen re-routes to the logged-in screen
So I think you and @claudiu are trying to explain something to me about post-mutation that I’m just not seeing 😅
I get that it’s for reshaping data, but it seems to allow for the routing change given the fulcro-template example. And when I do something else and the code recompiles (in dev config) the screen changes to the logged-in screen without my doing anything new to the state, so the router is responding to the route-to-impl!
at that point too
can't remember exactly if there's a gotcha 🙂 with refresh & post-mutation updates to state. 🙂
can you try instead of :refresh [:login/user]
something from the root component or is it there ? know I've had some similar issue but can't really remember.
Refresh is what you need. Post mutations have no attachment to UI, so you do need to say what it is changing
Ah that makes sense, I didn’t get that post-mutation updates don’t trigger refreshes of impacted part of the state tree. Putting :router
into the :refresh vector works!
Incidentally why doesn’t that work when I add the refresh as part of the mutation, rather than part of the load? e.g. before I did have
(m/defmutation login-complete [_]
(action [{:keys [state] :as env}]
(let [user-name (-> @state (get :login/user) second)
user (get-in @state [:staff/by-name user-name])]
(.log js/console user)
(when user (r/route-to-impl! env {:handler :PAGE/app}))))
(refresh [env] [:router]))
I made the same mistake recently…I’m toying with possible changes to rendering model. Fiddling with targeted refresh on post-mutations probably isn’t helpful…probably should just force a root render by default. There are so many optimizations already that this is probably just wasting ppls time
Post mutations are processed in a separate part of the stack. I would consider that a bug (listing refresh in mutation should “just work”)
But, again I think I’d prefer just doing a root render on post mutations. shouldComponentUpdate
is already a huge optimization. There’s no reason to limit post mutations to the transacting (load-running) component
@tony.kay want an issue raised on the possible bug with the defmutation’s refresh? I can add the bits of code etc above
I’d love benchmarks on the :keyframe
render mode on real apps…I think making it the default might just be the “right answer”
But just firing a root render would be much simpler as a mental model too, especially if shouldComponentUpdate
already has your back
I think I just decided: it should work as-is, but you can use :keyframe
render mode if you’d like simple model
the problem with doing root render is if you have some real-time complex UI that does a lot of networking to update some part of the UI…might lead to too much overhead, and there’d be no way to optimize it
If i'm doing scheen change routing. Triggering with component and follow-on reads is revommended, or theres a chance that using the reconciler could be just as fast ?
I think a lot of UIs can just use :keyframe
rendering mode and forget about follow-on reads altogether
much simpler code, and shouldComponentUpdate
will make 90% of apps plenty fast enough
If I get around to a book rewrite I’d probably make :keyframe
the default, and the follow-on read stuff with the current rendering mode a “and if you have performance problems, here’s what you can do” thing
The advantage of it being “the default” is that it gets tested a lot 🙂 And that optimization covers a lot of the complexity in the code.
But if it is just a forms-based app with proper union queries for routes, then I bet root overhead is low
@tony.kay that’s my use-case for this app - I might just go for :keyframe now in my fulcro-beginner hat
For loads when you enter a screen, is there a recommended place to put them. I used componentwilmount in the past. Now building my rounting to check for a protocol method on-enter
. Curious if there are better approaches to this, Keechma framework has start/stop on in its routing layer.
Still thinking aboit it. But like that in my component I have everything (query, ident, what it loads, caching logic) I can also call it from ssr. Will see how it goes once I get some more pages and usecases :)
For dynamicrouter. Is it designed to work more with :route :singleton. Tried a bit to get it working with :route :param/somth but seems like I have to install a new route for each param. Did I get that wrong ?
Yep. Although im doing it as on modules(grouped more routes) not routes so basically bypassing its load :)
so install for each route that is pre-loaded, and then when a module loads install for each route in that module
I noticed it adds a marker when installing. But if my route is :article :param/id . Then it looks like I have to load that in appstate, install it for that ident and then trigger the route change. Noticed the examples are with :article :singleton
how would the dynamic router know how to load your special identified instance of an article? It is meant for module loading, not instance loading
ahh true, makes sense. Think I was a bit confused because I only use dynamicrouter for changing screens. Routes have namespace and i make sure the module is loaded before calling (using shadow-cljs/loader).
@tony.kay would a layered approach be possible where :key-frame rendering is the default, but you can attach :refresh vectors on components as a performance optimisation, which the renderer would apply only to those components?
Then again, I think that is sort of what I’m saying: root refresh as sort of a default, and opt-in to the optimization
If the mutation declares a refresh, limit the refresh to things that query those data items; otherwise just render the whole thing
yep, and the decision has a much lesser impact on the codebase, rather than having to work through it to find all cases where a refresh is needed once you switch away from :key-frame
There is a convenience to the current model: the this
of the transact is always locally refreshed
you’d lose that, and would have to go about declaring it on a lot of mutations…but I guess that isn’t so bad. If it is performing ok, who cares?
Another possible down-side has to do with PR…at the moment the “default” rendering story is quite fast. Doing this would make it probably one of the slower UI libs out there (even though “slow” might be plenty fast enough)
FWIW if I knew a framework saved me developer time at a cost of 10fps (say) I’d be all over it - it’s my most precious resource
reminds me of https://hueniverse.com/performance-at-rest-75bb8fff143 haha
Hey guys, I’m experiencing some difficulty with a form, and I’m hoping someone can help me. I’ve pretty close to copied this example: http://book.fulcrologic.com/#_loading_or_creating_something_new and it is going pretty well. I have a form with subforms and the state seems to be updating fine on typing etc, but when I send the diff to the server, all values are empty strings or arrays. The strange thing is: when I fill in the form and trigger a reload via figwheel by changing something, it works! So my hypothesis is that for the transaction that is linked to the button onClick action (
:onClick #(prim/transact! this `[(submit-poll {:id ~(:poll/id poll-form)
:diff ~(fs/dirty-fields poll-form false)})])
), the syntax quoted part is ‘filled in’ when this is rendered, and I need to trigger a re-render of the component that submit button is in order to have the right diff sent to my server. But I don’t know how to make that component realize the form was updated.
At first I had the submit button in the form component itself, but that gave problems that the last updated field was not sent to the server. I’ve now moved the button outside of the form, exactly as in the example, but this gave me the problem as described. I’m suspecting it is something very small I am missing, but I’ve been staring blind at it for a day now 😉. Tips are appreciated!
Snippet below:@liesbeth I think you’re running into re-render problems…you see the functions in PollsHome are doing the submit, but the form changes are going on in the Form, which is rendering by itself
So, the data in your Home component is out of date when you submit, because it closed over the empty form when it rendered
The submit buttons should either be “on” the form, or you should force updates of the parent in your mutations on the form so it closes over the new state as it changes
Second question today that has to do with rendering optimizations that would be fixed using :keyframe
rendering…
Fulcro lein template updated. The new template sets keyframe rendering in the client, which might be better for beginners.
good call!
@oliver.mooney Try 2.5.10-SNAPSHOT on the declared post mutation refresh problem. I think that fixes it
i.e. you should be able to declare your refresh on the post-mutation now instead of on the load
Been on my mind for a while. Is the reason why aborting a load the state is replaced with nil internal or because its a better practice ?
fulcro inspect extension was updated (`0.0.8`), if you were in 0.0.7
just update on chrome (or wait for chrome to update itself), this brings database search feature, thanks to @tony.kay!
Aborting an active request stops the network transaction and acts as if the requested data resulted in an empty map from the server (so that load merge will overwrite the target with nothing). If this is a problem then target the load to a placeholder location and use a post-mutation to move it when load completes
@claudiu So, that’s the built-in behavior of a Fulcro http remote…but the abort call is abstract, so you could easily change it. In fact, we could add some kind of option to http-remote and let you choose. It could call the error-routing instead, which would then have the effect of doing nothing if there was no fallback or marker.