This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-03-05
Channels
- # aleph (90)
- # announcements (4)
- # aws (2)
- # beginners (61)
- # calva (2)
- # cljs-dev (8)
- # cljsrn (12)
- # clojars (4)
- # clojure (42)
- # clojure-europe (1)
- # clojure-finland (1)
- # clojure-greece (2)
- # clojure-houston (1)
- # clojure-italy (3)
- # clojure-nl (30)
- # clojure-spec (41)
- # clojure-uk (87)
- # clojurescript (44)
- # cursive (12)
- # data-science (11)
- # datomic (24)
- # defnpodcast (1)
- # fulcro (18)
- # hyperfiddle (1)
- # jobs (2)
- # juxt (11)
- # kaocha (3)
- # london-clojurians (5)
- # mount (2)
- # nrepl (2)
- # off-topic (9)
- # onyx (25)
- # other-languages (1)
- # parinfer (3)
- # pathom (5)
- # pedestal (3)
- # portkey (1)
- # re-frame (21)
- # reagent (24)
- # remote-jobs (7)
- # schema (2)
- # shadow-cljs (59)
- # spacemacs (5)
- # specter (7)
- # sql (9)
- # tools-deps (13)
So here's something that has been bothering me for a while now: Our team is working on a large cljs application which is based on re-frame and reagent. The subscription structure is heavily utilizing signal subscriptions which we have found to be a good decision as far as reusability, DRY-ness and simplicity is considered. However, we often run into the problem of requiring access to various parts of the state in the event handlers, with no simple way to access it. For example, I may have a subscription for :my-user/first-name, but no function capable of db -> my-first-name operation.
Currently we're solving this by using inject-sub -coeffect from vimsical/re-frame-utils, which allows injection of subscriptions as coeffects for event handlers. However, we could also choose to not use subscription signals but instead write db->data functions and nest those. That would lose a part of the simplicity (and caching), however.
I can't help feeling that re-frame is sort of split in two as far as best practices are considered. On one hand being able to use subscriptions as inputs for other subscriptions is great. On the other hand it makes writing event handlers more difficult. I'm also wondering if this is an issue that re-frame should eventually be able to solve by itself (without having to resort of inject-sub) , or if it already does but I'm just too blind to realize it.
@ipismai this questions pops up here every once in a while and AFAIK there’s no silver bullet. You pretty much described the alternative approaches above. Something to consider is why you need to do some-complicated-operation
in both, event handlers and subs, in first place? Sometimes it’s a good idea to store the result of some-complicated-operation
into the app-db. But that depends of course on the particular case at hand.
@valtteri I think a representative example would be for some-complicated-operation
to be the function build-user-entity
which is related to a user-editor view. user-entity
is built by merging together the existing data (retrieved through a subscription like [:user/entity-by-id <user-id>]) and the edits made (retrieved through a subscription that queries the app-db).
(rf/reg-sub ::edited-user-entity
(fn [db]
(let [{:keys [first-name last-name]} (get-in db [:path :to :editor :state])]
(cond-> {}
first-name (assoc :first-name first-name)
last-name (assoc :last-name last-name)))))
(rf/reg-sub ::user-entity
(fn [[user-id]]
:<- [::user/entity user-id]
:<- [::edited-user-entity])
(fn [base-data edited-data]
(merge base-data edited-data))) ; some-complicated-operation
The value returned by ::user-entity is then used to a) assert the validity of the data (used for UI visual feedback), b) as an input for the editor component (populating the input field values), and c) as the payload sent to the POST endpoint (injected via inject-sub). As far as I can tell, there is no way to accomplish storing the result of merging the two states without just moving the problem down another level (Having to use ::inject-sub when setting editor-state), or to completely stop using signal subscriptions.@ipismai In similar case I start editing with something like this
(re-frame/reg-event-db
::set-user-to-edit
(fn [db [_ {:keys [id]}]]
(assoc-in db [:admin :editing-user] (get-in db [:admin :users id]))))
And then the editor modifies the map under [:admin :editing-user]
directly. Subsequent events can pick it up from there and no some-complicated-operation
is needed.Hello everyone! In re-frame, if I only need data for a specific page, where should I dispatch the event to start loading the data? Don't want it to always load, only when on a specific page, but I also don't want to start dealing with component/did-mount
and hacks like that if I can avoid it
Hi @victorbjelkholm429! I dispatch events when routing to the page.
For example Reitit routing lib has “controllers” exactly for this purpose https://metosin.github.io/reitit/frontend/controllers.html
thanks! Yeah, so I would have to have some kind of API for dealing with that. Currently I have my own solution for routing
(defn notifications []
(let [messages (re-frame/subscribe [::notifications-subs/queue])]
(fn []
(into [:div
{:style {:z-index 999
:position "fixed"
:bottom 0
:padding 20
:display "grid"
:grid-gap 20}}]
(for [[id content] (reverse (take 2 @messages))
:let [open? (r/atom true)]]
;[(fn [])]
[mui/snackbar
{:open @open?
:Transition-props {:direction "right"}
:style {:position "static"}
:anchor-origin {:vertical "bottom"
:horizontal "left"}
:key id
;:auto-hide-duration 5000
:on-close (fn [_ reason]
(when (= "timeout" reason)
(reset! open? false)
(println "close" @open?)
#_(re-frame/dispatch [::notifications-events/remove id])))
:on-exited #(re-frame/dispatch [::notifications-events/remove id])
}
[:div
[:p {:on-click #(reset! open? false)}
"@open? " (pr-str @open?)]
content]])))))
I have an issue about for
. How to make this code to show on screen correctly? The issue is mui/snackbar
use transition, so when I use [fn () ...]
to wrap [mui/snackbar ...]
after each change of for
result it makes animation in again. It should do animation out only for notification which go out. But it do animation out for all notification and notification in for all notifications when anything change. How to do it correctly?I have queue in (re-frame/subscribe [::notifications-subs/queue])
and I want to show 2 notifications at once
Hmm it is so strange. I have feeling when I change r/atom
in second item. It change it at the same time in second and third (or first) one. Interesting. Like atom is changed for second item, but second item is recreated with different content, but reagent still think it is old item with old state.
(defn notification []
(let [open? (r/atom true)]
(fn [[id content]]
[mui/snackbar
{:open @open?
:Transition-props {:direction "right"
:timeout 2000}
:style {:position "static"}
:anchor-origin {:vertical "bottom"
:horizontal "left"}
:key id
;:auto-hide-duration 3000
:on-close (fn [_ reason]
(when (= "timeout" reason)
(reset! open? false)
(println "close" @open?)))
:on-exited #(re-frame/dispatch [::notifications-events/remove id])
}
[:div {:on-click #(reset! open? false)}
content]])))
(defn notifications []
(let [messages (re-frame/subscribe [::notifications-subs/queue])]
(fn []
(into [:div
{:style {:z-index 999
:position "fixed"
:bottom 0
:padding 20
:display "grid"
:grid-gap 20}}
[:p (pr-str @messages)]]
(for [msg @messages]
^{:key (first msg)} [notification msg])))))
^ solution