This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-07-31
Channels
- # architecture (1)
- # babashka (17)
- # calva (18)
- # cider (5)
- # clj-kondo (5)
- # cljdoc (44)
- # cljs-dev (2)
- # clojure (49)
- # clojure-europe (11)
- # clojure-norway (16)
- # clojure-uk (3)
- # clojurescript (89)
- # clr (8)
- # conjure (7)
- # cursive (26)
- # data-science (2)
- # datomic (15)
- # emacs (11)
- # events (1)
- # fulcro (8)
- # gratitude (3)
- # hyperfiddle (68)
- # introduce-yourself (1)
- # london-clojurians (1)
- # lsp (3)
- # nbb (8)
- # pathom (44)
- # pedestal (14)
- # polylith (2)
- # random (1)
- # shadow-cljs (8)
- # spacemacs (13)
- # squint (36)
- # tools-deps (9)
- # xtdb (17)
This is an absolute Beginner question: I'm trying to run a react project with Figwheel using Reagent. The project runs successfully but I'm getting these two errors in the browser and I'm not sure if this is an issue with dependencies. first error seems to be related to reagent:
dom.cljs:16 Uncaught TypeError: Cannot read properties of undefined (reading 'render')
at reagent$dom$render_comp (dom.cljs:16:6)
at Function.cljs$core$IFn$_invoke$arity$3 (dom.cljs:41:7)
at reagent$dom$render (dom.cljs:27:1)
at Function.cljs$core$IFn$_invoke$arity$2 (dom.cljs:36:5)
at reagent$dom$render (dom.cljs:27:1)
at re_frisk$ui$mount_internal (ui.cljs:86:6)
and the second error is related to material-ui:
material_ui.cljs:6 Uncaught ReferenceError: MaterialUI is not defined
at material_ui.cljs:6:46
Please help!If you check the location mentioned at the stack trace of the first error, you'll see that the run time can't find react-dom
. Maybe it wasn't installed at all (not sure why Figwheel would compile such a code in the first place), maybe it somehow ended up not being a part of the bundle, maybe the reference to render
got renamed.
The second error is of the same nature, just for a different symbol.
Unless you have very good reasons to use Figwheel, given that those deps come from NPM, I'd strongly suggest trying shadow-cljs instead. It offers an almost care-free development when working with NPM deps.
A note, just in case - the dependencies that come from NPM must be installed manually. Something might be using CLJSJS but it's deprecated and you should use NPM over that. Shadow-cljs maps CLJSJS dependencies over to NPM for you.
Thanks for the quick response @U2FRKM4TW! It's a very old project with very little meaningful documentation π’ . I tried to re-install the the node modules but that did'nt help either
what's strange about the project is that when Is that when I ran the project for the first time, it didn't build successfully. It seemed to complain about a missing file in the /dist folder named "index_bundle.js"
. there's already a file called "index.bundle.min.js"
in there.
It's only when I created an empty file with the same name that the project was able to finally build!
I'm not sure what the contents of this file should be but it looks like this is kind of like a known work around my previous colleague knew about.. since it's his project and he left years ago :face_palm:
Seems wacky. To me it would be easier and simpler to recreate the CLJS building pipeline using shadow-cljs and proper techniques rather than try and patch whatever you have right now only to have it re-done again at some point when you e.g. want to update some dependency (assuming you need this project long-term).

is index_bundle.js just the start point inside the index.html file perhaps, also as you have .min.js I wonder are there multiple builds like dev and prod perhaps for figwheel ?
Yeah , the project has a dev and a prod configuration from what I can tell with absolute beginner experience in Clojurescript
this is the the Project.clj file @UU67HFS2X
(defproject my-portal.ui "1.4.0"
:description "HT My Portal"
:min-lein-version "2.9.3"
:dependencies [[org.clojure/clojure "1.10.1"] ;; "1.8.0"
[org.clojure/clojurescript "1.10.773" ;; "1.10.520"
:exclusions [com.google.code.findbugs/jsr305]]
[com.google.code.findbugs/jsr305 "3.0.2"]
[org.clojure/core.async "1.2.603"] ;; "0.4.500"
[cljs-http "0.1.46" ;; "0.1.44"
:exclusions [com.cognitect/transit-cljs]]
[reagent "0.10.0" ;; "0.8.1"
:exclusions [cljsjs/react
cljsjs/react-dom
cljsjs/react-dom-server
cljsjs/create-react-class]]
[re-frame "1.0.0"] ;; "0.10.9"
[com.cognitect/transit-cljs "0.8.264"] ;; "0.8.256"
[com.bhauman/figwheel-main "0.2.11" ;; "0.2.3"
:exclusions [org.clojure/clojurescript
ring/ring-core
ring/ring-devel]]
[ring/ring-core "1.8.1"]
[ring/ring-devel "1.8.1"]]
:clean-targets ^{:protect false} ["resources/public/cljs-out"
"target"]
:aliases {"fig:pcdev" ["run" "-m" "figwheel.main" "-pc" "-b" "dev"]
"fig:pcprd" ["run" "-m" "figwheel.main" "-pc" "-O" "simple" "-bo" "prd"]
"build" ["with-profile" "+prd,-dev"
["do"
["clean"]
["run" "-m" "figwheel.main" "-O" "advanced" "-bo" "prd"]]]
"clean-start" ["do"
["clean"]
"trampoline" "run" "-m" "ht.fig"]
"start" ["trampoline" "run" "-m" "ht.fig"]}
:profiles
{:dev ;; development profile
{:source-paths ["src/main" "src/dev"]
:dependencies [[binaryage/devtools "1.0.2"] ;; "0.9.10"
[re-frisk "1.3.4" ;; "0.5.4.1"
:exclusions [org.clojure/clojure
org.clojure/clojurescript]]
[com.bhauman/rebel-readline "0.1.4"]
[com.bhauman/rebel-readline-cljs "0.1.4"
:exclusions [org.clojure/clojurescript]]
[cider/piggieback "0.5.0" ;; "0.4.1"
:exclusions [nrepl
org.clojure/clojurescript]]
[nrepl "0.7.0"] ;; "0.6.0"
[refactor-nrepl "2.5.0"]
[cider/cider-nrepl "0.25.2"]]} ;; "0.25.0-SNAPSHOT"
:prd ;; production profile
{:source-paths ["src/main"]}})
and then there are .edn files which i'm trying to figure out which library they are a part of
are the edn files by any chance the figwheel config ? not 100% on lein figwheel integration only used figwheel and shadow in isolation
i'm assuming that they are associated with figwheel
yes there are!
might see something like :output-to "resources/public/cljs-out/dev-main.js" but for the bundle file that your seeing output
yup you are absolutely correct
^{:watch-dirs ["src/main" "src/dev"]
:css-dirs ["resources/public/css"]
:auto-testing false}
{:main ht.dev
:preloads [re-frisk.preload]
:output-to "resources/public/cljs-out/main.js"
:external-config {:devtools/config {:features-to-install [:formatters :hints]}}
:infer-externs true
:npm-deps false
:foreign-libs [{:file "dist/index_bundle.js"
:provides ["react" "react-dom"
"create-react-class"
"material-ui"
"material-ui-styles"
"material-ui-colors"]
:global-exports {react React
react-dom ReactDOM
create-react-class createReactClass
material-ui MaterialUI
material-ui-styles MaterialUIStyles
material-ui-colors MaterialUIColors}}]}
okay so now that i'm reading this file closely
I think @U2FRKM4TW is in the right direction
but why is tthe index_bundle.js file listed in foreign-libs is something i'm not getting
I do wonder if its worth just updating the config to shadow might be worth it, not to many libs but then you can use things like npm directly. yeah @U2FRKM4TW is more knowledgeable on these things π
Definitely can you run me through the changes i'd have to make π . There's also the risk of breaking the CI-pipeline but getting the project to run is a bigger concern at the moment haha
Given :foreign-libs
, it seems that the build is a multi-staged one.
The first stage uses NPM dependencies to build index_bundle.js
that has nothing but the required NPM libraries bundled together.
The second stage builds the CLJS code while pointing it to index_bundle.js
for all the JS symbols that are mentioned in :provides
and :global-exports
.
The first stage might be done with webpack, check what package.json
has in its "scripts"
section.
> There's also the risk of breaking the CI-pipeline Oh! Just check out what the pipeline does then? No need to guess.
@U2FRKM4TW, package.json is missing a scripts section entirely
I'm not sure how my collegue ran webpack .. maybe separately just once
Does it have anything in devDependencies
or maybe dependencies
that you don't use in CLJS code? Specifically looking for webpack or a similar tool.
But yeah, better to consult the CI scripts if they also build the CLJS code.
"devDependencies": {
"webpack": "^4.41.0",
"webpack-cli": "^3.3.9"
},
"dependencies": {
"@date-io/date-fns": "1.3.13",
"@material-ui/core": "4.8.0",
"@material-ui/pickers": "3.2.8",
"create-react-class": "15.6.3",
"date-fns": "2.8.1",
"react": "16.8.6",
"react-dom": "16.8.6"
}
I have a feeling that my colleague probably built the dist file just once
manually
and placed it there
let me try doing that
Right. There's also the "start"
alias in :aliases
that looks suspicious. You'll have to check what the mentioned namespace does. Perhaps that's the thing that starts webpack.
omg ... that was actually it. I manually built the dist file with webpack and now the project works :face_palm: π
@U2FRKM4TW right , I'll defo check that out. This experience definitely warrants a complete re-write with Shadow-cljs. But for now I'm just happy that it finally worked. Thank you so much for your help @U2FRKM4TW and @UU67HFS2X. Had a good first experience in this community π π
Is this an okay way to do network requests in CLJS? Or is it more/less typing than it needs to be? Ignore that I'm not handling errors yet
I'd use promises instead of core.async
. Especially if you don't have any other reason to use core.async
.
Is that interop or a cljs library?
Interop with js/Promise
. The underlying network requests use it anyway if you use js/fetch
(the modern API for network).
Minor, unrelated nitpick: if you expect to call m/validate
often, then it may be beneficial to compile the validators ahead of time and use that in your code
Not sure what you mean. Will it not be compiled when I deploy the app?
Malli exposes m/validator
and m/explainer
, both of which accept a schema and produce a validation and error explanation function, respectively
I believe you always pay the cost of producing a validation function every time m/validate
is ran. So if you expect to run m/validate
very often, it may be beneficial to pre-compile the validator function
That is to say,
(def valid-thing? (m/validator thing))
then you use (valid-thing? obj)
wherever you want to validate obj
against the thing
schemasee https://github.com/metosin/malli#validation for more details
core async's <p!
is more idiomatic, imo. Let's you write sequential code / no callback functions in a go block and will play nicely with things that return promises.
And will balloon your bundle size if you never use core.async anywhere else. IMO it's less idiomatic because of incidental, unneeded, complexity. And it does not actually play that nicely with things that return promises because you can't chain go
and promises, you'd have to use js/Promise.resolve
explicitly.
I actually experimented with using <p!
a while back and found only 10.5 kb increase to the (uncompressed) bundle size.
To me, the real issue is that promises and channels are semantically different, and it is ultimately a mistake to convert between them. That is to say,
(defn chan->promise [c]
(js/Promise. (fn [resolve reject]
(try
(resolve (<! c))
(catch js/Error e
(reject e)
(close! c))))))
looks tempting and it may work in simple cases, but I would not rely on it10 kB is surprising, to be honest. I just tested it - with a bare-bones project a single (a/go (js/console.log (ai/<p! (js/fetch "
with no other usages of core.async
adds 78 kB (10 kB after compression). But some of the dependencies are outdated, I should test it again after updating them.
if your only use of core.async is for dealing with promises you definitely should not be using core.async
In a real world app sized codebase fetching and sending data are common and having pipes to place and read data to and from is a reasonable abstraction. I'd absolutely use core.async for that.
Many web app patterns rely on some storage where the rest of the system observes changes to the data in there. In those cases, a response handling part of an HTTP request is just putting that data into the storage.
And for many web apps, that isnβt the case, or the plumbing that supports that benefits from operations you could perform on your data using core async patterns (transducers, pub/sub, pipelining, etc, etc). Chances are some part of my ClojureScript app is going to use core async (either directly or a library). The dep will be there, saving 10KB is unlikely worth excising it.
Hello everyone π Our team built a new kind of data structure in ClojureScript (resembles an object system with inheritance, self-ref and stuff like that). Expectedly, we started to encounter performance issues. Our implementation is quite naive ATM & based on standard persistent maps, vectors, lookups (and even multimethods). Our bread and butter are maps, lookups and conjs. Does anyone here have experience with performance tuning / performant data structures and can share some insights / knowledge with us? π Thanks everyone, Have a great day/night βΊοΈ
Your description is quite vague but you can always use your browser's profiler to see any bottlenecks. (Assuming you're running that code in browsers).
Yeah, we're just getting into all these performance measurements. We are looking for any useful guide that can guide us in this territory.
Without knowing the specifics and size, my first guess is always intermediate values causing gc pressure π
So transients to the rescue π¦Έ Thanks π I'll add some more pointers: β’ We're using metadata map to store internal structure data (we can use deftype fields instead). β’ We're lookinup in a tree like structure ALOT (ATM with a simple unoptimized recursion) β¦ It does terminate early when it should though β’ We do use specter though which suppose to give a performance boost for intricate selects and transforms. β’ We experimented with multiple dispatch on this project so ALOT of our functions are defmulti β’ Most of our maps along the tree are PersistentArrayMaps (<9 entries) should we switch to HashMaps or any other performant map? β’ lookups are memoized but I'm not sure if optimally
IMPORTANT, WEIRD (?) BEHAVIOR:
β’ When lookingup a complicated value (using the REPL) the first time it takes ~4 secs.
β’ If I'll try to look again I'll get it in 0ms because of memoization.
β’ However, if I save (triggering shadow's hot-reload) and look again, now it takes 10secs, the next time 16 secs...
β’ Repeating the process 3-4 times and the runtime crashes.
Just in case - note that Specter increases the bundle size quite significantly. At least, it used to, around a couple of years ago, so I stopped using it when building for browsers. Regarding array maps - they're array maps when having that few entries specifically because they perform faster than hash maps. Multimethods have their own performance penalty. In any case, don't do guesswork - measure instead. So far it's just staring into a crystal ball.
π@U2FRKM4TW Alright, so starting in the browser's profiler. Are there any cljs specific things I should look for or will it stare me in the face?
The latter. The flamegraphs are rather straightforward unless, perhaps, you do lots of async stuff.
How big are these data structures, by the way, and how are they populated?
Hmmm I'm not sure how's a good answer to that question looks like, but I just counted 614 nested keys in a "heavy" structure
So {:key1 {:key2 {:key3 ...}}}
all the way to 614?
What kind of data is that? Again, just looking at the crystal ball here, but is there a chance it would benefit from normalization?
No, the keys are kind of normally distributed
Just to avoid any misunderstanding - normalization has nothing to do with the normal distribution. It's a data layout concept that doesn't depend upon the actual values.
Actually, it very well might, normalization is an idea I haven't thought about π π
Hey guys, jumping in. After a short investigation we suspect a memory leak that causing the performance issue. Can you recommend for a good tool for identifying memory leaks? @U2FRKM4TW @U05092LD5
Browsers have lots of tools built into them for memory stuff. E.g. you can take two heap dumps and ask the browser to show the objects allocated between the two. You can then find the largest objects, store them as global variables, and view those as CLJS data using cljs-devtools.
Perhaps another potentially useful thing here is Flowstorm debugger but I don't know whether it has affordances for memory usage debugging. @U0739PUFQ would know.
I have instrumented memory allocation on timeline and I can see that 2 constructors are leaking, both "Object" and "Array". How can I go deeper from here?
Not sure what you mean by "on timeline". At least Chrome's memory profiler doesn't seem to have it.
Here's an example of what I see. temp1
is the top object above and it tells me that the most memory is taken by the undo/redo mechanism of re-frame. temp2
is the second object and it shows a data-heavy section of app-db
that got changed between heap snapshots.
I meant that the profiler I ran was of type "Allocation instrumentation timeline". By viewing each snapshot I see that the number of objects I have is getting bigger (I execute the same code in the REPL). I have successfully stored it in a global var and I can look into the object. Thanks I'll update soon
BTW, just in case - I use normalized data and in one project I pretty much never clean the old data when a user requests some new data, just because cleaning it properly is not that easy and it doesn't affect the target users. Even if the whole website takes 1 GB of RAM, there are no slowdowns. It's all hashmaps, it's always O(1).
> Perhaps another potentially useful thing here is Flowstorm debugger but I > don't know whether it has affordances for memory usage debugging. @U0739PUFQ would know. it currently doesn't contain anything specifically related to memory leaks, since I tend to use heap dumps in the jvm and browser. That said, since you can access all your recordings from the repl, maybe you can write some Clojure to count values with specific shapes etc, and then jump to where those values were created, and stuff like that to identify the leak. One thing you could try is execute an action, then group-by and count all recorded values by some fn, then execute the next action and repeat the same and try to figure out how values of different kinds are growing. This is something that you can't normally do with heap dumps if you are using maps everywhere, since everything has the same type (all entities are just maps), but you should be able to examine it with FlowStorm and the repl.
Hey guys thanks for your help, we found the leak and fix it.
It was a -add-method
with a broken logic.
I just gave it a little more thought to what I described earlier about using FlowStorm to trace leaks and it is flawed, and it is probably not possible since the whole recording business is about leaking everything, so just forget about what I said.
What would be nice to have in Clojure is a way of exploring a heap dump that instead of grouping things by Class, can group by a provided fn, so you can distinguish different kind of entities when everything is represented as maps