react

"mulling over state management stuff"
Aron 2020-05-30T06:08:31.262200Z

what do you mean 'scope'? is that the same as redux's store?

Aron 2020-05-30T06:08:59.262700Z

scope in js means 'access to names', that is, a namespace, is that the same thing in cljs?

dominicm 2020-05-30T06:34:02.271200Z

There's a similarity with what I'm asking and js scope. I'm asking about which components define and manage which pieces of state. Reframe uses a global scope. Redux uses tree-local scope. Etc.

Aron 2020-05-30T11:21:19.277600Z

I earlier gave an analysis of this matter which I claim is complete, each react component have 3 distinct states in might handle. 1. state that is displayed, this includes hardcoded values like text or hiccup/jsx/html/element tagname/attr , but it also includes state that is derived from external sources but still has to be displayed in some manner 2. state that is necessary for its internal functioning, logic, business imperatives, but it is not something that is directly driving the output, so this is not like a flag to show/hide, more like a bunch of pre-baked percentages to calculate a number, or numbers/functions that are used to calculate positions, styles. 3. state that it has to send to third part, typically your own backend but also any other public services has some expectations on what it needs and that is under this category I don't believe that there is need for more categories here, and so far no one has tried to point out any state that is not under either of those (would be interesting to see such an example though!) Without further rules over how your components are built, potentially any react component can have any or all of these, what makes little sense to have a component that has neither.

Aron 2020-05-30T11:22:27.278600Z

Although this is still not the most useful probably, but it helps me to see what I am doing if I can put them in their right place since they usually are read and written at the same place/time

Aron 2020-05-30T11:22:48.279100Z

Sometimes I try to omit one (usually the last one) and it's always a mess 😄

Aron 2020-05-30T11:24:00.280300Z

I am not experienced with cljs, but I expect these could have their own namespace/spec/tests and the rest of the app should use them, but since I am coming from js where circular dependencies are sometimes ok, it's probably not going to work just as easily

Aron 2020-05-30T11:25:22.281900Z

it also don't really make sense to me to use a different namespace's values in a simple react component, but I guess this is more just aesthetics, react is allowing "too simple" constructions that are not complex enough to be useful, but are simple enough to start out looking very promising and very neat

Aron 2020-05-30T11:28:13.284Z

Now, you can put either of these three into either local or global scope, 1. 2. people don't really like in global, but then 3. is never this explicit, if you try to make just that global then you need some kind of system to connect the two

Aron 2020-05-30T11:28:34.284500Z

we at work use a custom js react hook called useElmish that mimics the Elm architecture

Aron 2020-05-30T11:29:08.285300Z

but any effector system that decouples effects from dispatches is fine, so your state updates are pure, I am probably getting overly detailed, so I stop 🙂

alidlorenzo 2020-05-30T16:10:59.293400Z

@lilactown found out yesterday what you meant when you said macros can’t return macros. I thought I had some clever way of doing it by having a component functions return syntax quoted data and then having a render macro expand them, but all of the ways I tried couldn’t that to work, always some nested form that wasn’t evaluated. I guess now I know why Helix works the way it does 😅

😄 1
2020-05-30T18:39:04.295Z

A macro can return a call to a macro though

2020-05-30T18:39:20.295400Z

Which will continue to be expanded further

lilactown 2020-05-30T18:41:49.298100Z

that is true. what I’ve attempted before (and ran into issues) is macros defining other macros, e.g. (defnc my-component []) would create a new macro my-component in the namespace. doesn’t work in CLJS due to macros needing to exist in a CLJ namespace

2020-05-30T18:42:21.298900Z

But ya, no higher order behavior with macros. You can't return one and say pass it around to something else to use.

2020-05-30T18:44:40.299800Z

Hum, ya that's interesting. ClojureScript does throw additional ranches into it

alidlorenzo 2020-05-30T19:01:34.302Z

@didibus but if the macro symbol that was returned is dynamic couldn’t get it to work

alidlorenzo 2020-05-30T19:02:36.302400Z

considered trying eval but that’s not bundled by default in cljs

2020-05-30T19:02:45.302800Z

Ya that's the ClojureScript limitation (I believe). All macros must be expanded at compile time, and in ClojureScript, compile time can not be interleaved with runtime.

alidlorenzo 2020-05-30T19:12:00.318100Z

honestly what I’m trying is something i feel should work but i figure doesn’t because of extra Clojurescript caveats

alidlorenzo 2020-05-30T19:12:08.318500Z

just to get it off my mind i’ll put it here

alidlorenzo 2020-05-30T19:12:17.318800Z

like lets say this is inside a macro (component props)` where component can be a function or a macro, important bit is that it returns more macro data, i.e. (compiler props children) it seems like since component was dynamically called, even though compiler is a macro it’s not expanded and so in clojurescript runtime i’m just left with that unevaluated s-expression

lilactown 2020-05-30T19:14:03.319200Z

I don’t know if you can pass in a macro as a value like that

alidlorenzo 2020-05-30T19:15:48.320700Z

it works for another use-case I tried that was one level deep, but here i think because it’s a macro/function call that outputs more macro data (2 levels deep), couldn’t get it to work

lilactown 2020-05-30T19:17:39.322Z

hmm yeah I just tested this: (defmacro foo [] “asdf”) (defmacro bar [s] `(~s)) (bar foo) ;; => “asdf”

lilactown 2020-05-30T19:17:56.322500Z

that’s in self-hosted CLJS tho - I’m on mobile using Replete

2020-05-30T19:19:12.322800Z

That piece of code, it doesn't make sense

2020-05-30T19:19:37.323500Z

You're escaping all the unquote. You might as well write (component props)

alidlorenzo 2020-05-30T19:20:16.324Z

those are dynamic arguments (wasn’t a full example, just for sake of discussion)

2020-05-30T19:20:49.324900Z

Ah ok, also, ya I'm wrong as well. It does delay the function call till later

2020-05-30T19:21:25.325300Z

What do you mean here by dynamically called?

2020-05-30T19:21:48.325900Z

I suspect it might be the unquote qualified expansion that is messing it up

2020-05-30T19:22:29.326600Z

I'm also testing in self hosted replete though :p so can't say for sure

2020-05-30T19:22:42.327100Z

But I think unquote expands to a ClojureScript namespace

2020-05-30T19:22:53.327700Z

But your component macro has a Clojure namespace

2020-05-30T19:23:30.329Z

What if you fully qualified it to the exact Clojure macro?

alidlorenzo 2020-05-30T19:24:12.330500Z

no the issue wasn’t that it wasn’t being called (example @lilactown posted for example works)

alidlorenzo 2020-05-30T19:24:19.330900Z

the issue was that the output wasn’t being called

alidlorenzo 2020-05-30T19:25:08.332200Z

so say (~component ~props) expands to (my-component {}) and that’s a function that once called outputs (compiler {} children) .. that last part, compiler , is also a macro that I want macroexpanded

alidlorenzo 2020-05-30T19:25:14.332500Z

2 levels deep, not one

2020-05-30T19:26:44.333Z

Oh, but with a function in the middle? Ya that won't work

2020-05-30T19:27:04.333700Z

Because the function will evaluate later, after the JS has already been compiled

alidlorenzo 2020-05-30T19:28:03.334400Z

yea that’s what I’ve learned. but the necessary info is available at compile time so I was hoping it would work

2020-05-30T19:28:19.335100Z

So compiler is treated like a function by ClojureScript as well, and then you'll just see the return value of unquote

2020-05-30T19:28:49.335600Z

Well, why not make my-component a macro?

alidlorenzo 2020-05-30T19:29:03.335900Z

same result

2020-05-30T19:29:29.336400Z

Or return `(compiler component ~props)

alidlorenzo 2020-05-30T19:30:07.336700Z

hmm i didn’t try that

alidlorenzo 2020-05-30T19:30:56.337400Z

kinda reluctant to try lol, working with cljs macros sucks up so much time

alidlorenzo 2020-05-30T19:32:33.338200Z

going to try it later and let you know if it works, thanks for suggestion @didibus

2020-05-30T19:34:14.338700Z

Ya macros in cljs are extra confusing.

2020-05-30T19:35:39.339600Z

Though I'm really surprised that 2 level deep macros didn't work, feel that should for sure work

alidlorenzo 2020-05-30T19:39:03.339700Z

is macro expansion expected to be recursive? i.e. macro outputs another macro s-expression, so the output is* also macro-expanded? i know it technically could work (info available at compile time) but figured it’s just not allowed

alidlorenzo 2020-05-30T19:55:36.340700Z

thinking about it, idt this would work since I need to access to that args that are closed in inside the component function

2020-05-30T20:25:08.340900Z

It is ya

2020-05-30T20:25:37.341100Z

Outer macros expand first, and then it recursively expands until there are no more macros

2020-05-30T20:26:16.341300Z

So the outer macro expands, and the code it returned is then also macroexpanded, etc.

2020-05-30T20:26:25.341500Z

At least in Clojure

alidlorenzo 2020-05-30T20:40:45.343100Z

this is a simpler alternative that shows a summary of issue with nested macros in code

(defmacro defnc "Create component with given `tag` name and `body`."
     [tag _bindings & body]
     `(defmacro ~tag [props#]
        ;; This nested expression needs to be syntax quoted so we can pre-compile props 
        ;; But since it is two levels deep then it isn't evaluated as cljs code by the Clojure compiler.
        ;; So at runtime, rather than having a fn call that creates a js object, we have a list of Clojure data.
        `(.createElement react Fragment ~props# ~~@body)))
so even if we can call nested macros dynamically, their nested output isn’t actually evaluated

👌 1
alidlorenzo 2020-05-30T20:45:03.343500Z

yea that’s how it works if the syntax quoted macro definitions are static, but not sure if that’s meant to work if they are dynamic

2020-05-30T22:01:37.343900Z

In Clojure it would, because macros can expand at run-time as well

2020-05-30T22:01:47.344100Z

But not in cljs

2020-05-30T22:05:03.344300Z

Though I feel even on ClojureScript, nested macros in macros should still expand recursively at compile time

2020-05-30T22:28:00.344600Z

Try with clojure.core/defmacro