This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-01-11
Channels
- # ai (1)
- # announcements (59)
- # aws (3)
- # babashka (16)
- # beginners (36)
- # calva (29)
- # cider (7)
- # clj-kondo (33)
- # clojure (77)
- # clojure-austin (1)
- # clojure-australia (4)
- # clojure-europe (16)
- # clojure-france (7)
- # clojure-nl (2)
- # clojure-spec (1)
- # clojure-sweden (8)
- # clojure-uk (4)
- # clojurescript (17)
- # cloverage (4)
- # conjure (2)
- # cursive (3)
- # datomic (22)
- # emacs (16)
- # fulcro (10)
- # graphql (3)
- # helix (13)
- # jobs (1)
- # kaocha (1)
- # lsp (7)
- # malli (1)
- # nextjournal (1)
- # off-topic (32)
- # pedestal (8)
- # polylith (5)
- # reitit (4)
- # reveal (1)
- # shadow-cljs (67)
- # spacemacs (7)
- # tools-deps (6)
- # xtdb (4)
Thinking through using local state in grove conditionally. Something like this:
(defc ui-node [local?]
(bind {:keys [id data] :as props} (get-in @state-atom [:id 1]))
(bind local-data (when local? (atom data)))
(bind data (if local?
(g/watch local-data)
data))
...)
would get me in trouble, https://github.com/thheller/shadow-experiments/blob/master/doc/components.md the hook type changes if local?
changes. What I'm trying to achieve: if local?
, work with local state (initialized to the value of global state), else work with global state.yes this would not work. bind has pretty much the same rules as react hooks or the svelte stuff
(bind {:keys [id data] :as props} (get-in @state-atom [:id 1]))
this should also never be done. that will only ever deref once so it'll never updates if you change the value in state-atom
(bind {:keys [id data] :as props} (get-in @state-atom [:id 1]))
was a stand-in for a Fulcro hook. If I do a watch on state-atom
then I'll chain two AtomWatch
es, which is a no-no 🙂
So, I can move my conditionals to render
, though this means that
(bind local-data (atom data))
(bind local-data-watch (g/watch local-data))
will initialize needlessly in instances where local state is not needed. I would also need to do
(bind local-data (atom (get-in @state-atom [:id 1 :data])))
to avoid the AtomWatch
chaining, but that is what I want: I want to read this only on initialization of the instance.
It seems to me like local state is the right 'pattern' for what I need (state per component instance). Storing it in a central atom/db for each instance seems like overkill (would also have to handle component lifecycle). This is how I had it set up in Fulcro with initLocalState
. Happy to hear suggestions on how to handle it differently 🙂as I said local state is not recommended. neither in fulcro nor in grove. first maybe explain WHY you want local state?
it costs pretty much as much as storing it in a local atom, so it is definitely not overkill
if this is about handlings forms I have a lot of plans for that but nothing implemented yet 😛
> it costs pretty much as much as storing it in a local atom, so it is definitely not overkill
I agree, but would also have to handle component lifecycle (add/remove it on component mount/unmount).
The use case is: the :data
attribute is stored in a central db. Depending on some conditions, instances of a component using this attribute should either:
1. share this value in the central db (i.e. read current value from and update it in central db)
2. or read and store the value only on component instance initialization, then only update the value tied to that instance. Remove the value (wherever it is stored) once the component instance is destroyed.
ok as a general rule of thumb. grove is very much about the concept of "your view is a function of your data". therefore your data drives your components/views. your components should NEVER create data on mount or destroy it on unmount.
instead some part of your data should cause the mount of a component and removing such data should cause your component to unmount
that entire concept somehow got lost in react along the way but it was the promise at the start
it does require rewiring your thinking a little bit but I find it immensly useful personally
of course there is some state that is derived out of your view such as the height of a div or the mouse position or whatever. those are exceptions and bind and local state work for those
fulcro is very similar in that regard. giving you even more tools to do this such as the forms and uism support
It's currently very difficult to imagine how this would be a practical approach in this case, but I'll try 🙂 Thank you!
I mean the same stuff still happens. its just not your component mount triggering it.
you still haven't provided much info on "in this case". would help if you have some sample code. in fulcro is fine or JS or whatever is fine
Right, sorry, thought I was being clear. You know the 'case': deep recursive trees and toggling. Now, in certain contexts, toggle state should be shared globally between component instances, in other contexts the toggle state should purely be local and transient. Every component shares the same data/state on initialization (and most of the data is 'global'), but there are slight deviations in behaviour depending on context.
:expanded-in-contexts #{:foo :bar}
:expanded-for #{[:some/ident 1] [:other/ident 2]}
the goal is that if you have all your data at hand you know exactly what your view/ui looks like. no guessing.
Yeah, I understand that and working with Fulcro was so nice because of that. I'm sure there's a way to represent it as data, but it is much simpler (easier? :)) to use local state (in this case). And, in Fulcro, I can always look at the value of that state as well 🙂
The examples you've provided don't work, unless the idents identify the actual component instances. I.e. I have to track :expanded
for every component instance. There is absolutely no sharing (in certain contexts). The context simply defines whether the state should be shared globally, not what it should be.
So, the way I see it, representing it as data would be, in some global atom, {id-of-component-instance {:expanded true}}
.
there is no such thing as id-of-component-instance
. there is id-your-created-in-data-somewhere
that is passed to your defc
component as a prop
I always design data first. meaning I create some EDN data until its in a shape I'm happy with. then I create something that renders it, maybe tweak it again until everything looks good 🙂
Forgive my ignorance on cljs issues… but how does one fix warnings like this:
"5.1.0" was required by jar:file:/Users/rick/.m2/repository/com/kiranshila/cybermonday/0.3.139/cybermonday-0.3.139.jar!/deps.cljs
NPM dependency "remark-parse" has installed version "9.0.0"
I confess the relationship between npm/yarn/shadow-cljs/clojurescript and clojurescript deps from mvn etc isn’t exactly clear to meDoes this help? https://shadow-cljs.github.io/docs/UsersGuide.html#_missing_js_dependency
I don’t know; it looks to me like that’s a different problem. Mine appears to be saying that there is a version mismatch; not that the dependency is missing.
I have a bunch of other similar warnings to do with different deps
Not sure there’s an easy fix. If you are on tools.deps build, you can fork cybermonday and update its deps, and depend on it as a git coord.
Ok I think I see what the issue is… looks like someone on my project added the transitive npm deps directly to our package.json
😆
It’s funny you should say that; In parallel I’ve been trying to upgrade shadow-cljs; but I can’t move beyond 2.15.12
without getting test failures.
I’m not sure what happened in 2.15.13
that is causing them but I’m assuming it’s somehow related to the closure/clojurescript version bump and perhaps I need to do something with the flag :global-goog-object&array
:thinking_face:
Good to know if I upgrade they’ll vanish though :thumbsup:
Not to mention all the different javascript module systems etc
Is there a way to stop shadow from starting socket REPL and nrepl when in embedded mode?
I'm trying to figure how to have a plain Clojure environment that uses shadow as a library
(start! {:nrepl false :socket-repl false})
yes but note that pretty much all in shadow-cljs assumes the presence of a shadow-cljs.edn
. so if you attempt to make that not a thing you will lose features 🙂
Or probably since the server start! doesn't need the builds, I can pass it explicitly in.
Oh we have a shadow-cljs.edn file with our builds defined, nothing else (apart from the deps which is what I want to take over)
Ah, turns out you need to let shadow start nrepl because it injects its own middleware into things. I don't suppose that you can do that outside the context of a shadow build, right? Or to phrase it differently, can I pass in command line arguments to nrepl.cmdline such as that would make connecting to a CLJS REPL work?
middleware are always added. CIDER adds its own. Here’s how you can start up an nrepl server with cider and shadow middleware:
/usr/local/bin/clojure -Sdeps '{:deps {nrepl {:mvn/version "0.6.0"} cider/piggieback {:mvn/version "0.4.2"} cider/cider-nrepl {:mvn/version "0.24.0"}}}' -m nrepl.cmdline --middleware '["cider.nrepl/cider-middleware", "cider.piggieback/wrap-cljs-repl", "shadow.cljs.devtools.server.nrepl/middleware"]'
more details at https://github.com/clojure-emacs/cider/issues/2812 if you want more