thanks, that's very similar than to what I see
my bundle is 755kb total š
Makes sense. Thx.
any idea when you might cut a release for Helix, at all? I'm currently working around https://github.com/Lokeh/helix/commit/45c9cdf4c93ba81f178be43c6bc69ffcd3c7b505 in my $ macro and also have custom class behaviour, so both changes would be welcome to see released :)
yeah, I wanted to ask about "the release cycle" as well, I just didn't want to be rude : S
@dominicm is react* refresh working for you with the commit @lilactown linked above?
iām unable to get it working with defnc
factory
I haven't tried yet :)
Question for people in here. On state management in general, in React, and helix. I see how hooks are excellent for component state management. You have use-state for local state in something like an input, or use-reducer for more complex state that might be shared by multiple components. What about global app state? Do people favor re-frame/redux and having a nice centralized app db? Or trying to keep state where it is needed? I see good arguments for both. In the React world, Redux seems really efficient for everything other than local component state, since you give only the props that are needed. I guess people are just using all of them? Or is anyone trying to get by with just hooks and passing props where necessary?
there was a recent discussion about global state (https://clojurians.slack.com/archives/CRRJBCX7S/p1589820944328100) basically state management options are in a flux bc concurrent react has been unstable, so best to stick with hooks + context API if you can, since itās easier to migrate out of that
Thanks for sharing. I actually read that, but had the same thoughts as the poster, that context would make sense if not for the fact that consumers re-render on state changes. So it is best for simple things like an app theme, which doesn't change frequently.
I agree with you also, I am leaning towards sticking to plain React as much as possible for ease of migration later, which basically means use hooks/props. Hmm, guess I will do that and re-evaluate if necessary later.
Maybe if I just make sure context is close to the components that need it, then it won't be that bad. Thanks, that was helpful.
Please report back if you can or cannot get it to work... I havenāt had time to test it thoroughly
@tekacs iāll do a release this in an hour or so
my opinion is that āglobal app stateā isnāt the right problem to think about
there are many kinds of āglobalā state that might want different solutions
if you break it down like: ā¢ theme ā¢ navigation ā¢ data cache ā¢ notifications ā¢ modals each of these are cross-cutting concerns for your application, but can have very different requirements from each other
@krzyzowiec thereās also libraries that make it easier to use react context and avoid rerenders one Iāve tried out in a past js project is constate: https://github.com/diegohaz/constate
@lilactown True. I like the simplicity of the idea of dealing with them the same way, but you are right that they are not the same.
I cannot get it to work. At first I thought it was because project uses React Native Web but even with a basic counter using helix dom it does not rerender
@alidcastano Ty, I will check that out
example counter code Iām using:
(defnc App
[]
(let [[count set-count] (h/use-state 0)]
(d/div {:on-click #(set-count (inc count))}
(d/button {:on-click #(set-count (inc count))} "Add")
count)))
(defn ^:export mount
[el]
(rdom/render (App) (js/document.getElementById el)))
this works when defnc
has helix factory off, but now when it has it on
can you show me the macroexpand when factory is on?
iām on mobile atm, I can pull my computer out here in a few mins
I think that if we were to put on our Rich Hickey hat, that itās āeasyā to put it all in one place, but āsimpleā would be to manage them separately :D
anyway I know that itās a non-answer, since I donāt really have a recommendation for how to manage all of those
what makes sense to me right now is to put it all in context, and hide it behind a hook that I can change to use a mutable store later if perf starts to suck
Right, interesting. Does that mean you would (if starting a new app) try to avoid using re-frame?
I can see now, thinking about it, that context would work better if you made sure to keep concerns narrow and have few consumers. (if you assume state updates will be frequent enough to affect performance)
hm example above just prints (App)
(macroexpand '(App))
ah sorry, try macroexpanding the defnc
(do
(if goog/DEBUG (def sig36351 (helix.core/signature!)))
(def
c-render-type
(clojure.core/->
(clojure.core/fn
c-render-type
[props__33703__auto__ maybe-ref__33704__auto__]
(clojure.core/let
[[]
[(helix.core/extract-cljs-props props__33703__auto__)
maybe-ref__33704__auto__]]
(if goog/DEBUG (clojure.core/when sig36351 (sig36351)))))
(clojure.core/cond->
(clojure.core/true? goog/DEBUG)
(clojure.core/doto
(goog.object/set "displayName" "cljs.user/c")))))
(def c (helix.core/cljs-factory c-render-type))
(clojure.core/when
goog/DEBUG
(clojure.core/when sig36351 (sig36351 c-render-type "" nil nil))
(helix.core/register! c-render-type "cljs.user/c"))
c)
this looks correct..v0.0.11 is out!
what specifically is that testing?
i.e. I see the {:updatedFamilies {,,,} :staleFamilies {,,,}}
in the console
but
1) if I change some text I donāt see it update
2) it isnāt showing me whether hook state is preserve, which is why I used the counter example
I was testing just that editing the text updated
I just tested that state works too
weird copied and pasted same code, might be my shadow-cljs setup :thinking_face:
yeah I wouldnāt use re-frame
you can pass an event emitter into context too
similar to what redux does
but I would create custom hooks for specific things, and pass in the global state via context
e.g.:
(def theme-context (helix.core/create-context {,,,}))
(defnc theme-provider
[{:keys [children]}]
(let [[current-theme set-theme] (hooks/use-state {:mode :light})]
($ (.-Provider theme-context)
{:value current-theme}
children)))
(defhook use-theme
[]
(let [current-theme (hooks/use-context theme-context)]
,,,))
the implementation of use-theme
and theme-provider
is completely hidden from your components, so you can later have it use a global atom that you pass into context instead of local state
Iām uploading a repo so you can look at it
Oh that's fantastic ty! That clears up a lot for me.
donāt know why I didnāt try this earlier but nuked all deps in my monorepo and itās working
appreciate the help
rough, but Iām glad itās working now
sure thing. it forced me to actually test my code, too š
I just had a really dumb idea
what if you could write inside of a component:
(component-a {:foo "bar"}
(component-b (some-fn "baz")))
and defnc
walked the body of your component, looked for any lists starting with a symbol that had some metadata that it was a React component, and expanded it to:
($ component-a {:foo "bar"}
($ component-b (some-fn "baz")))
I was attempting to do something similar but then started using hicada with helix instead
I already have the same concept working with https://github.com/Lokeh/helix/pull/58
the nice thing about your example is it works with custom components, whereas hicada only converts the primitives youāve hooked up into it
but i find for custom components iām just calling them as factory functions
I think the sad path is the tricky part. if someone tries to call a component like a function, but it doesnāt have the correct metadata on it, then it will fail at runtime with a variety of errors because youāre literally invoking the component as a function
is this the PR you meant to link? i see the ^memo
and ^callback
helpers not the $
stuff
yeah it is. what I meant is that the same concept that allows ^:memo
and ^:callback
to be expanded to (use-memo ,,,)
and (use-callback ,,,)
can be applied to calling components like (component ,,,)
and expanding it to ($ component ,,,)
I was thinking maybe you could catch/warn for those errors for simple cases cause youād know whether itās a custom component or not.. but i guess you wouldnāt know whether it is or isnāt a callable function
as a side note, maybe the functionality shouldnāt be overloaded in $
but some other utility like $>
so people that use it can be aware of shortcomings
it wouldnāt be overloading $
, I would just walk all the forms inside defnc
and do the transformation there
that way you could do weird things like:
(let [el0 (my-component {:number 0})]
(<> el0
(for [n (range 1 10)
:let [elN (my-component {:key n :number n})]
elN)))
and all those calls to (my-component ,,,)
would get expanded to ($ my-component ,,,)
inline
otherwise youād have to do:
(let [el0 ($> (my-component {:number 0}))]
(<> el0
(for [n (range 1 10)
:let [elN ($> (my-component {:key n :number n}))]
elN)))
which doesnāt seem as usefuloh I meant youād use $
for single components but $>
for multiple that youād like converted into $
but i think i like your example better
my favorite pattern for components is to export a reducer š
and some event handler functions that can be configured with namespaces so different versions of the same component although use exactly the same code to maintain state at the same time, they write in different places
context is not slow for simple SPA navigation and basic form validation - interactions