Team, Iโm experimenting with Cljs with re-frame, smooth-ui. And kind of fiddling to get a simple UI component to show-up. It works on a particular case and not when I just wrapt it in a function. Posted the details in https://stackoverflow.com/questions/67715254/cljs-re-frame-is-not-showing-element-as-expected. Any guidance would be helpful.
I have put [
in both the places. Still facing the same issue.
My bad, I also had to put the [
around the function call that just generates data.
(defn app
[]
[grid [generate-data]])
Thatโs a huge help. Also I found the article very helpful.. Thanks a lot @dpsutton!
do [grid (generate-data)]
and [cell {:id "1-1"...}]
square bracket [
versus a paren
Is there any reason why binding a r/atom
in a reagent component wouldn't work?
What do you mean?
Are you meaning derefing it with @
?
This works fine:
(defonce items (r/atom []))
(defonce text (r/atom ""))
(defn component [{:keys [title on-delete]}]
{
:pre [(s/valid? :component/title title)]
:post [(s/valid? :component/title title)]
}
[styled/root
[styled/details
[:span title]
[styled/delete-button {:onClick on-delete} "Remove"]]
[styled/submission {:onSubmit (fn [e] (.preventDefault e) (swap! text inc))}
[:p (str @items)]
[:p (str @text)]
[:input {:type "text" :on-change (fn [e] (swap! text #(str e.target.value)))}]]])
But not this:
(defn component [{:keys [title on-delete]}]
{
:pre [(s/valid? :component/title title)]
:post [(s/valid? :component/title title)]
}
(let [items (r/atom []) text (r/atom 0)]
[styled/root
[styled/details
[:span title]
[styled/delete-button {:onClick on-delete} "Remove"]]
[styled/submission {:onSubmit (fn [e] (.preventDefault e) (swap! text inc))}
[:p (str @items)]
[:p (str @text)]
[:input {:type "text" :on-change (fn [e] (swap! text #(str e.target.value)))}]]]))
You need a barrier to not redefine the atoms on each render. They need to be defined once and then changed in the render
Oh I fixed it:
(defn component [{:keys [title on-delete]}]
{
:pre [(s/valid? :component/title title)]
:post [(s/valid? :component/title title)]
}
(let [items (r/atom []) text (r/atom 0)]
(fn []
[styled/root
[styled/details
[:span title]
[styled/delete-button {:onClick on-delete} "Remove"]]
[styled/submission {:onSubmit (fn [e] (.preventDefault e) (swap! text inc))}
[:p (str @items)]
[:p (str @text)]
[:input {:type "text" :on-change (fn [e] (swap! text #(str e.target.value)))}]]])))
So apparently I needed to return a new function and not just the view layout
I wonder why it works that way.
You need something callable upon the change happening
Shadow-cljs has this rectangle that shows in the bottom of the screen when there are warnings in the code. I'd like to add one to my project too. What terms do I google to find instructions on how to create something similar? Or does anyone have an example code snippet I can play with?
@endrebak85 basic DOM stuff like animations, transitions, transforms. the code for the shadow-cljs stuff is here https://github.com/thheller/shadow-cljs/blob/master/src/main/shadow/cljs/devtools/client/hud.cljs#L54-L139
but it is not react based. so a react/reagent based solution would look a little different
oh what I linked is the loading animation. if you actually just want the box at the bottom thats easier. just an absolute or fixed positioned div, https://github.com/thheller/shadow-cljs/blob/master/src/main/shadow/cljs/devtools/client/hud.cljs#L233-L237
I also wondered about that cool logo. If I ever have something that takes more than a second to render I'll use something similar.
{:a (foo)
:b (bar)
,,,}
if I were to call a function with this literal over and over again, could there be times when (foo)
and (bar)
calls swap order?no, but the order they eval in might not be what you have in the code logically. if it reaches the hash-map threshold that is.
๐:skin-tone-2: I'm ok with that as long as it is stable
if you care about ordering maps are typically not a good idea. thats why bindings (eg. let) typically use vectors.
in this case I was thinking through using React hooks in props maps, i.e.:
($ some-comp {:foo (use-foo) :bar (use-bar)})
and was trying to determine if I should write a rule in helix's linting to warn on usageI think thats discouraged in react? too easy to end up in conditional branches (when stuff? ($ some-comp {:foo (use-foo) :bar (use-bar)})))
If {:foo (use-foo) :bar (use-bar)}
is always a literal map and $
is a macro, then you could write the macro to guarantee consistent* ordering
@smith.adriane I'm not sure that's true since the map literal assigns its ordering at read-time, which is after the $
can get ahold of it
@thheller somewhat true, but helix will detect you're calling a hook inside of a conditional and emit a compiler warning in that case
JS linters also will call that out in the respective JS syntax
the map literal must be read before it's passed to $
, right?
right
I might be confusing things, but I thought something similar to the following would guarantee consistent ordering
(defn with-order* [m]
(let [body (into {}
(map (fn [[k v]]
`[~k ~(gensym)]))
m)
bindings (into []
(comp (map (fn [[k v]]
`[~(get body k) ~v]))
cat)
m)]
`(let ~bindings
~body)))
(defmacro with-order [m]
(with-order* m))
(macroexpand-1 '(with-order {:foo (use-foo) :bar (use-bar)}))
;; (clojure.core/let
;; [G__44218 (use-foo) G__44219 (use-bar)]
;; {:foo G__44218, :bar G__44219})
I might even consider sorting the bindings by key to make bindings even more stable across time/compilations/versions of cljs
I am trying to use with-redefs
to mock a function. However, the function has multiple arities and only the first one is mocked. The original function gets called when the respective number of args are passed. Is this a known limitation of with-redefs?
Hmm, a simple example works as expected
@cpmcdaniel doesn't seem possible?
arities are on the fn itself - so when you redef the fn those won't be there
I think somehow the original function value is getting closed over where it is being called with the second set of params
also doesn't seem possible you need to create something minimal
when you redef you are fully replacing the old thing - there's nothing to close over
How far does idiomatic clojure take the "just use a map" approach to function arguments? In React, JSX.Element
is basically syntax sugar around a function which takes a map (`props`) as its input. But I'm not sure if I ever tried using just maps for any other part of my code (e.g. my service logic).
every Clojure system I ever worked on primarily used this pattern
ClojureScript itself is like some 45,000 lines of code and is just EDN data + fns
I kind of get the thinking of it. You don't need type constructs like Options or Maybes to express optionality, just leave the key out of the map.
there's some records + protocols lying around in ClojureScript from the old days but those are one of the less pleasant parts of the codebase now
Yeah I kept hearing "protocols + records" a lot, but I couldn't find anything modern on it that was still advocating it.
Seems like it was a trend, like core.type
it is useful - but in very restricted circumstances
I like specs a lot more because you can specify a lot more than what ADTs can signify.
They're basically dependent types... except it's not checked at compile time obviously.
But the problem with dependent types is that it gets really messy really quick. It's one reason why you don't see them in Haskell. They can't figure out a way to do it elegantly.
Can you link any clojure/script repo that you feel like presents good, modern, idiomatic Clojure? E.g. runtime polymorphism instead of protocols and records, maps instead of positional arguments, et cetera?
specs are nice and work as advertised - we have two years of them - we've found it most useful in a system - i.e. client / servers - less certain about other cases
:pre
and :post
are also still quite useful
I'm using :pre
and :post
strictly for react components right now. Anything that I plan on reusing. And I use it sparingly, sometimes preferring default values.
@rosenjcb I don't have anything off the top of my head, I think maps and collections scale quite well - you can say whatever you like about JS / React - but I think it has shown with taste and good engineering fns + simple data scales pretty dang well
so this is not even specific to Clojure really
Honestly, JS has kind of teased me into some of the founding principles of Clojure.
I think you could easily convince a React developer that Clojure's ideas of maps and heterogenous collections, immutability is good.
I came to Clojure from JS - basically Clojure had everything I like about JS minus the stuff I didn't like
but that's the tip of the iceberg
the only reasonably popular language carrying on the dream of real interactive development is Clojure(Script)
I still don't have a good developer workflow with Clojurescript. Right now I just use shadowcljs + lein. It's not much different from my JS development, but Ive been wanting to see how I can do some repl based stuff as well.
Cursive works well - on newer hardware like M1 IntelliJ is not slow - the other options I can't speak for since I generally avoid anything that cannot leverage clojure.main
style REPLs
Oh I just use vs code and vim.
for UI stuff the text-editor integration is less annoying because of hot-reloading
One person suggests doing this:
write code as .cljc as much as possible including tests
evaluate it as if it were Clojure (using my current VS Code/Chlorine/Reveal setup)
use Figwheel Main for the cljs-into-the-browser aspect
re-frame, cljs-devtools
that's another reasonable compromise
Chlorine/Clover works really well with Shadow-CLJS, to be honest ๐
I guess it allows you to keep logic separated from what's mostly view code. Since .cljc code is supposed to be "platform neutral", right?
(But I'm the author, so maybe I'm biased ๐)
The problem is that not always that simple to just write "platform neutral" code on ClojureScript. JS runtime is async, and there's not "await" for it, so sometimes you need to do "promise dances" and other complicated things. On Chlorine/Clover, as it's 100% ClojureScript, I even wrote a version of "async matchers" that will timeout tests after a while, teardown, etc...
Ah i see. Yes youโre right
I think that with figwheel or shadow-cljs, the browser development is already pretty good. I use Emacs with CIDER but for clojurescript I think shadow-cljs hotreloading already does quite a lot
and karma for watching tests in a terminal... basically the setup you get with the re-frame template is amazing :D
@dnolen I suspect my issue is related to the use of async code within the with-redefs block (specifically js/Promises). Is there another way to achieve mocking via :before and :after fixtures?
OK, I was able to use set! in :before and :after fixtures to achieve what I wanted
Thatโs what I was about to suggest :)
with-redefs isnโt going to work with async code
Oh which tempalte is that? I use this https://github.com/reagent-project/reagent-frontend-template
@rosenjcb I use this for re-frame https://github.com/day8/re-frame-template
I'm running into a strange behavior with Clojurescript macros and the default build system, and I want to know if this is expected behavior or if I should file a bug. It seems like the default Clojurescript build system doesn't pick up changes to a .clj file that supplies macros. For example:
cat src/test/* && clj -M -m cljs.main --target node -c test.main && cat .cljs_node_repl/test/main.js && node .cljs_node_repl/main.js
;;;; macro.clj
(ns test.macro)
(defmacro libMac [] "test.macro/libMac 1")
;;;; main.cljs
(ns test.main (:require-macros [test.macro]))
(println "STARTING test.main")
(println (test.macro/libMac))
// Compiled by ClojureScript 1.10.773 {:target :nodejs}
goog.provide('test.main');
goog.require('cljs.core');
cljs.core.println.call(null,"STARTING test.main");
cljs.core.println.call(null,"test.macro/libMac 1");
//# sourceMappingURL=main.js.map
STARTING test.main
test.macro/libMac 1
This works as expected. But if I change macro.clj
to "libMac 2"
and run the same build command, the resulting main.js
still says 1
. I need to delete the main.js
to get the system to rebuild it after each macro change.