This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2016-10-06
Channels
- # aleph (79)
- # bangalore-clj (3)
- # beginners (49)
- # boot (74)
- # cider (10)
- # cljs-dev (21)
- # cljsrn (2)
- # clojure (105)
- # clojure-berlin (1)
- # clojure-brasil (1)
- # clojure-dusseldorf (1)
- # clojure-korea (1)
- # clojure-poland (3)
- # clojure-russia (38)
- # clojure-spec (146)
- # clojure-uk (20)
- # clojurescript (70)
- # cloverage (1)
- # component (1)
- # core-async (23)
- # css (16)
- # cursive (22)
- # datascript (1)
- # datomic (22)
- # defnpodcast (6)
- # emacs (60)
- # events (1)
- # hoplon (94)
- # jobs (1)
- # jobs-rus (13)
- # luminus (11)
- # off-topic (11)
- # om (48)
- # onyx (5)
- # proton (7)
- # re-frame (87)
- # reagent (39)
- # rethinkdb (1)
- # ring-swagger (14)
- # rum (6)
- # specter (14)
- # untangled (105)
- # vim (6)
- # yada (22)
I'm working on this today
for the context of a single API call validating an input field, I'm keeping the state in a ratom along with a few other bits of info
@superstructor: we use a :loading as a count of requests in progress
interesting, thanks everyone. @len what do you do to ensure the count is accurate; e.g. different paths or chains of multiple handlers causing requests / many failure & success handlers etc ? I guess you could do it in the http fx handler itself ?
@superstructor thats the big tricky problem isnt it 🙂
indeed 🙂
re-frame gives you the mechanism, but ensuring that the data you have is meaningful is up to the application
my only thinking on it so far, which I have not verified in any way, is to add a :num-in-flight
or similar value feature to the http fx handler, so you can receive that somehow and do what you want with it. If it is tracked internally in one place, I guess it is less likely that someone will forget to update the loading count in their success/failure handlers ?
still that has a problem that would give no context to what requests are in flight, so if you have two unrelated parts of the page loading it might not be very useful :thinking_face:
yes thats the thing that om.next / grapql approaches. you try to collect all requests together on the client into one
Although the docs focus on app startup, it can also work for coordinating multiple async tasks at other times too.
Attempts to use something like :num-in-flight
counters are, at core, attempts to create little FSMs.
re-frame-async-flow-fx effectively creates a FSM too:
1. it internally maintains state about "what has happened so far" (what events have already been seen)
2. it uses further events
as triggers to drive the FSM forward towards success states or failure states
It is all about state
and events
:-)
is the current recommendation to define event handlers in events.cljs
instead of handlers.cljs
?
if so, can the leiningen template be updated? I'm writing a #lambdaisland episode on re-frame, and I'd hate it to be out of date already 🙂
We've made that change already but it might not be released
Here we go: https://github.com/Day8/re-frame-template/pull/34 the change will be in the next release
Thanks.
BTW, the other area in which our docs are lagging is around subscriptions
This is the best document currently available on the new reg-sub
capability:
https://github.com/Day8/re-frame/blob/master/examples/todomvc/src/todomvc/subs.cljs
Yep, you got it
I'll do my best, the first episode won't go very deep, I'll focus on specific aspects in later episodes
Is there a way to abort a handler within an interceptor? I see ways of pre/post manipulating the data but not a way of stopping subsequent dispatches.
Are you after debouncing ?
Oh wait. i just reread your first sentence
I'm looking to validate the input with Spec and stop any updates if the data isn't right. I can do that within the handler functions themselves but that means a lot of repeated "Return db or the db in the world map"
So there is a way for an interceptor to pretty much abort the further processing
A context looks like this: https://github.com/Day8/re-frame/blob/master/docs/Interceptors.md#what-is-context
Notice the :queue
and :stack
Within an interceptor you can manipulate them
99.9% of the time you don't touch them ... you just let the interceptor chain "run"
But, if an :after
changes the :queue
it will change what further interceptors run
The :queue
is what interceptors are yet to run
(def stop-chain
(re-frame.core/->interceptor
:id :stopper
:before (fn [context]
(assoc context :queue [])))
Yes, sorry, :before
(not :after
as I said above)
Note: the :stack
of interceptors (already run) would be unwound
So if you wanted to be really brutal about stopping everything you'd clear BOTH the :queue
and :stack
Okay, that sounds exactly like what I need, even the stack unwinding. Thanks for the explanation. On a side note, is it possible to pass parameters to interceptors when they're "attached" to a handler? All the examples I've seen have a vector representing the chain but that's it.
Plenty of examples in: https://github.com/Day8/re-frame/blob/2ccd95c397927c877fd184b038e4c754221a502d/src/re_frame/std_interceptors.cljc
You'll want ...
... actually look at path
It allows you to do this:
(reg-event-db
:some-id
[ (path [:here :and :there]) ] ;; <--- I'm assuming you want something like this
(fn [_ _]
....))
We call them interceptor factories
They are a function which returns an Interceptor, who's :after
and :before
close over the args passed in
I'll give you a correction to that stop-chain
above ... give me a minute ...
(def stop-chain
(re-frame.core/->interceptor
:id :stopper
:before (fn [context]
(-> context
(dissoc :queue)
(re-frame.core/enqueue []))))
:queue
is actually meant to be a Queue
Not simply a vector
Gotcha https://github.com/Day8/re-frame/blob/master/src/re_frame/interceptor.cljc#L108
Yeah, there should be a better API for clearing the queue. But that's the best I'm got for the moment
(further adjustment made to the above code)
Is there any way in re-frame to subscribe to a subscription outside of a component? In my case the app needs to update the push notification configuration of the underlying platform whenever certain parts of the database are updated. I could make it so that each event handler that performs these database updates also directly update the notification configuration as a side effect, but it would be a much better fit imo if I could register subscriptions on the side that do the notification updates independently.
@looveh I get this request a bit. Almost an FAQ. The re-frame way is that event handlers cause state changes, nothing else.
Or, to be more correct, either the event handlers or their interceptors
I do understand the initial attraction of having something "subscribed" to app-db
which then causes further side effects.
But re-frame is only reactive around the axis of v = f(s)
(view is a function of the state).
Instead of thinking subscriptions, you should put an interceptor on all event handlers which might cause the app-db
changes
That interceptor can see if a change has been made and, if they have, can then trigger the necessary further effects.
@mikethompson All right, I can see your point, thanks!
Have a look at the on-change
Interceptor
It might not be exactly what you want but it will be indicative. You can then writ your own.
Application architecture question: I'm making a hobby game, and I want to keep game logic separate from presentation. I am implementing the game state as an atom, and game state changes as transitions on it, but I want to keep it all in CLJC files, have it use plain old atom
, etc, so I can run tests in Clojure and possibly use the logic outside of the web.
I'm not entirely sure how to incorporate this into a re-frame application. I guess the smartest approach is not to tie the game logic to the atom
type, but instead to write game logic which just deals in game-state hashes. Then a re-frame app could start with something like:
(def app-db
{:game (game/initialize), :ui {}})
;; the :ui hash is just a catch-all for non-game-related webapp state
...while a Clojure app could say...
(def game-state (atom (game/initialize)))
...and tests would all just say...
(let [game-state (game/initialize)]
(testing "some behavior"
(is (correct? (transition game-state somehow)))))
...I think I may have answered my own question. I guess I'll hit enter in case anyone has better advice!idk if it's a proper way of doing things, but in my latest application I've offloaded nearly all the the handler logic to a seperate cljc file of pure functions (including fx, handled monadically)
that way, basically every piece of business logic is a a pure function, testable in Clojure
@superstructor re: requests in flight: this is exactly what I did, with an exception of tagging every outgoing request with UUID so I can track which request comes back
I'm wondering what's a better way to handle routing with bidi. Context: you have some Foos that have some Bars, so URLs are like /foo1, /foo1/bar1, /foo2/bar2, etc. I have two options in mind: A) define a static and fairly simple routing table like ['/' {[:foo-id {[:bar-id ''] :bar}] :foo}] and handle non-existing foo-ids and bar-ids in handlers (fire a request to a backend, check if foo and bar exist, etc.) B) on webapp start pull in a general structure of foos and bars, form a routing table, initialize bidi, perform a match and then do business as usual
I hope it's a clear explanation of a problem. What do you think? In my particular domain it's possible to pull a full list of Foos and Bars, so B is feasible.
if I weren't also using bidi for server-side routing I'd go with https://github.com/funcool/bide instead