This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-09-27
Channels
- # announcements (2)
- # asami (25)
- # babashka (124)
- # beginners (46)
- # calva (55)
- # cljdoc (70)
- # clojure (68)
- # clojure-australia (2)
- # clojure-dev (63)
- # clojure-europe (38)
- # clojure-nl (1)
- # clojure-spec (1)
- # clojure-uk (8)
- # clojurescript (56)
- # community-development (4)
- # conjure (1)
- # copenhagen-clojurians (1)
- # core-async (1)
- # cursive (3)
- # datahike (5)
- # datomic (183)
- # depstar (2)
- # figwheel-main (10)
- # fulcro (20)
- # honeysql (2)
- # hyperfiddle (1)
- # integrant (68)
- # jobs (6)
- # jobs-discuss (5)
- # juxt (1)
- # malli (13)
- # off-topic (8)
- # pathom (2)
- # rdf (10)
- # reagent (11)
- # remote-jobs (1)
- # rum (1)
- # shadow-cljs (69)
- # spacemacs (1)
- # sql (5)
- # tools-build (51)
- # tools-deps (6)
- # xtdb (24)
Hi I'm looking to use integrant on the frontend where I'm lazy loading in modules made up of components. The components need to be initialized in. a specific order. I'm trying to figure out how to use integrant for the following: I have three components comp-a
,`comp-b`, and`comp-c` . Both comp-a
and comp-b
depend on comp-c
and need it to be initialized be for they are. I have two modules, mod-a
and mod-b
which contain comp-a
and comp-b
, respectively. If I can't control the order of when the modules load is there a way to initialize comp-c
in whichever module loads first and then use its reference in the other module (without initializing it a second time). For example, if mod-a
loads first, comp-c
and comp-a
are initialized , then mod-b
loads and only comp-b
is initialized and the reference of comp-c
from mod-a
is used?
> For example, if `mod-a` loads first, `comp-c` and `comp-a` are initialized , then `mod-b` loads and only `comp-b` is initialized and the reference of `comp-c` from `mod-a` is used? Yes, both comp-a and comp-b will use the same value returned by comp-c init-key. Meaning that comp-c only gets evaluated (initialized) once
It seems to me that the order is valid though. So I'm not sure what the problem in your scenario is
What are you using to render your components? (we use reagent with integrant at my work)
As I understand it we need to create two systems by calling ig/init
in both mod-a
and mod-b
. If I don't control which system starts first how can share comp-c
? Is there a way to merge systems?
We use re-frame with reagent, what I'm trying to do is to make sure events and subscriptions are loaded before they're used.
All right, if two components share 1 ref (in this case, comp-c
) does comp-c
need to emit an event twice?
no, I want to register the event handler once and in comp-c
and then from comp-a
and comp-b
I want to be able to dispatch the event knowing that the handler exists
Appreciate it!
let me try to get some put together
To me it sounds likes you're doing something like this?:
(defmethod ig/init-key :comp/a [_ {:keys [comp-c]}]
(fn []
[:div "From A - Comp C: " comp-c]))
(defmethod ig/init-key :comp/b [_ {:keys [comp-c]}]
(fn []
[:div "From B - Comp C: " comp-c]))
(defmethod ig/init-key :comp/c [_ opts]
(re-frame/dispatch [:initialize/comp-c opts])
(let [comp-c (re-frame/subscribe [:db/comp-c]) ]
(fn []
[:div "Comp C: " @comp-c])))
So you have 3 (reagent) components. a and b reference component c. component C dispatches an event at initialization (once) and returns a reagent component (fn) with its own sibscriber
But either should work. The events are only dispatched once and comp-c has access to the subscription within its fn context
more this
(defmethod ig/init-key :components/comp-a [_ {:keys [post-alert-event] :as opts}]
(rf/dispatch [post-alert-event "comp-a alert"]))
(defmethod ig/init-key :components/comp-b [_ {:keys [post-alert-event] :as opts}]
(rf/dispatch [post-alert-event "comp-b alert"]))
(defmethod ig/init-key :components/comp-c [_ _]
(rf/reg-fx :post-alert (fn [_ [_ title]] ...trigger alert...)))
adding more to the example .. one min
no it's the reg-fx that's synchronous
do I don't want dispatch to be called until reg-fx is
assuming comp-c init is called first that example will work fine, but I think we're getting to far into implementation details. Backing up the real issue I'm trying to figure out is how to control when comp-c
is initalized if I want something like this
(ig/init {:comp/a {:comp-c #ig/ref :comp/c}
:comp/c {}})
(ig/init {:comp/b {:comp-c #ig/ref :comp/c}
:comp/c {}})
this part :comp/c {}
is wrong
yes, the real case is I have my app which calls ig/init and then the user navigates to some section the requires lazy loading a module , I then need to ig/init all components in that module but they often have deps that are already init originally by the app
does that make sense?
Really appreciate your help by the way! Trying to be concise and clear but also forming the picture in my head at the same time
I think what you're saying is that you want to initialize part of your config, and reuse the initialized parts later
yes, that's a better way of putting it
I don't think integrant has any way of handling this situation. Integrant was created to build your entire system at initialization, not with lazyloading in mind
If you try to initialize a specific component, it will drop any other components that aren't referenced
(ig/init {:comp/a {:comp-c ( ig/ref :comp/c)}
:comp/b {:comp-c ( ig/ref :comp/c)}
:comp/c {}}
[:comp/a])
;; => {:comp/c :c
;; => :comp/a [:a :c]}
But even if the keys (`comp/b` in this case) were still available, the next ig/init
would fail because it would initialize :comp/c
again instead of returning the value
hey, I appreciate any ideas you have!
(ns app
(:require
[integrant.core :as ig]
[integrant-tools.core :as it]))
(set! *print-namespace-maps* false)
(defmethod ig/init-key :comp/a [_ opts]
[:a (:comp-c opts)])
(defmethod ig/init-key :comp/b [_ opts]
[:b (:comp-c opts)])
(defmethod ig/init-key :comp/c [_ opts]
;; This is only called once
(println :initialize/comp-c)
:c)
(defn call-subset [config ks]
(let [config-subset (ig/init config ks)]
(doseq [k (keys config-subset)] (remove-method ig/init-key k))
(it/derive-unknown config-subset ig/init-key :it/const)
(merge config config-subset)))
(let [config {:comp/a {:comp-c (ig/ref :comp/c)}
:comp/b {:comp-c (ig/ref :comp/c)}
:comp/c {}}]
(-> config
(call-subset [:comp/a])
(call-subset [:comp/b])))
;; Result:
;; => {:comp/a [:a :c]
;; => :comp/b [:b :c]
;; => :comp/c :c}
So in this scenario we have the same config / init-keys as before (but now with a println to check if :comp/c
gets called once or twice). We create a new function call-subset
which only calls the required keys. Afterwards to removes all ig/init-key
multimethods of the results keys, and derive those keys from :it/const
(same as :duct/const
, just return the opts
). Finally we merge the new subset config into the original config.
Link to integrant-tools: https://github.com/kwrooijen/integrant-tools
looks to be right, now I just need to grok it
Music to my ears
I think the one downside to this (though not a huge problem) is needing to define the dep tree all up front
config {:comp/a {:comp-c (ig/ref :comp/c)}
:comp/c {}}
(-> config
(call-subset [:comp/a])
(merge {:comp/b {:comp-c (ig/ref :comp/c)}})
(call-subset [:comp/b]))
Seems like I need to play with it more. It's actually not me but another member of my team who's offline, I've now spent more time on this than I should have 😬. Really appreciate all the help @kevin.van.rooijen!! We'll test it out and I'll let you know how it goes. Hope you don't mind if we return with more questions 😅
Generally it looks like it should work, just wondering if I'm missing anything between toy code and PoC implementation with our app
Thanks! I'll update you when I learn more!