Fork me on GitHub
#clojurescript
<
2021-01-29
>
Nazral05:01:08

Hi everyone, I have a question regarding file upload from drag & drop. I do it this way: https://git.sr.ht/~tyruiop/constellation/tree/main/item/viz/src/main/viz/sidebar.cljs#L233 And it works perfectly fine with firefox, however with Chromium datatransfer remains empty no matter what I do, any idea how to solve it? All the solutions I found on stackoverflow for javascript just didn't work Nevermind, the problem came from Wayland, it works just fine with Xorg

Adam Helins09:01:16

Is there a guide somewhere about what the Closure Compiler has trouble with when eliminating code?

thheller09:01:09

anything with side effects or what it thinks may be side effects 😉 not aware of more precise docs

👍 4
Adam Helins09:01:40

I think it struggles with laziness in general, probably fearing side effects. For instance it doesn't seem to remove things like (def foo (concat ...)) That might explain why my advanced builds are usually fat and seem to include quite a lot of unneeded junk (looking at Shadow's report). So I am wondering if it's worth the trouble to wrap top-level vars in some way, or wrap those top-level values in a (delay) for instance.

thheller09:01:29

yes, laziness is not something it understands. (delay) works

Adam Helins10:01:21

Then library authors should know about this as it is not something you think about too much unless you're doing a lot of CLJS.

Grigory Shepelev10:01:01

I'm working on a small personal blog app on clojurescript using reagent and reitit as a router. For some reason component isn't updated when atom is. There is a post on Reagent docs on this proble. But it didn't helped: https://cljdoc.org/d/mthomure/reagent/0.8.1-custom-components/doc/frequently-asked-questions/why-isn-t-my-component-re-rendering- May be someone ran into same problem or it's something obvious that I can't see. Here is the code for my post:

clojure
(ns posts.second-post
  (:require [cljs-http.client :as http]
            [cljs.core.async :refer [<! go] :as async]
            [reagent.core :as reagent]))

(defonce d
  (reagent/atom nil))

(defonce request
  (go
    (let [responce (<! (htt p/get "" {:with-credentials? false}))]
      (reset! d responce))))
      
(def post 
  {:meta {:slug "first-post"}
  ;; UPDATE DOESN'T HAPPEND HERE!
   :content [:span (str @d)]})
Here is the post view:
clojure
(ns blog.views
  (:require [blog.config :refer [conf]]
            [reagent.core :as reagent]
            [clojure.string :as string]
            [reitit.frontend.easy :as rfe]
            [blog.posts :refer [posts]]
            [cljs.core.async :refer [<! go]
            ...]))

(defn post
  [match]
  (let [{:keys [path]} (get match :parameters)
        {:keys [slug]} path
        requested-post
        (first
         (filter
          (fn [p]
            (= slug (get-in p [:meta :slug]))) posts))]
    (if (some? requested-post)
       [:div  (get requested-post :content)]]
      [:span "Post not found")])))
And here is the code for my core (and router):
clojure
(ns blog.core
  (:require [blog.views :as views]
            [reagent.core :as reagent]
            [reitit.frontend :as rf]
            [reitit.frontend.easy :as rfe]
            [reitit.coercion.spec :as rss]
            [spec-tools.data-spec :as ds]
            [reagent-material-ui.core.container :refer [container]]))

(defonce match (reagent/atom nil))

(defn current-page
  []
  [container
   (if @match
     (let [view (:view (:data @match))]
       [view @match]))])

(def routes
  [["/"
    {:name :frontpage
     :view views/home}]
   ["/post/:slug"
    {:name :post
     :view views/post
     :parameters
     {:path
      {:slug string?}
      :query
      {(ds/opt :lang) string?}}}]])

(defn start
  []
  (rfe/start!
   (rf/router
    routes
    {:data {:coercion rss/coercion}})
   (fn [m]
     (reset! match m))
    ;; set to false to enable HistoryAPI
   {:use-fragment false})
  (reagent/render
   [current-page]
   (.getElementById js/document "app")))

(defn ^:export init []
  ;; init is called ONCE when the page loads
  ;; this is called in the index.html and must be exported
  ;; so it is available even in :advanced release builds
  (start))

(defn stop []
  ;; stop is called before any code is reloaded
  ;; this is controlled by :before-load in the config
  (js/console.log "stop"))

p-himik10:01:38

I don't see how exactly you're using that posts.second-post/post.

p-himik10:01:39

Also, consider creating code snippets instead of code blocks when your code is rather long. The snippets are collapsed by default which makes it much easier to navigate the rest of the channel.

Grigory Shepelev10:01:07

Thnx. I will try snippets next time.

Grigory Shepelev10:01:30

(ns blog.posts
  (:require [posts.first-post :as first-post]
            [posts.second-post :as second-post]))

(def posts
  [first-post/post
   second-post/post])

p-himik10:01:45

I need to see the view that ends up using posts.second-post/post. Anything else is irrelevant, at least right now.

p-himik10:01:47

A ratom change causes a view to re-render when both of these conditions are met: - That ratom was deref'ed in that view - That view function was used with [...] and not with (...)

thheller10:01:30

the trouble is most likely holding on to a reference of other namespaces that aren't directly reloaded

thheller10:01:15

see "Holding Code references in State"

thheller10:01:33

(def posts
  [first-post/post
   second-post/post])

Grigory Shepelev10:01:40

@U2FRKM4TW

(ns blog.views
  (:require [blog.config :refer [conf]]
            [reagent.core :as reagent]
            [clojure.string :as string]
            [reitit.frontend.easy :as rfe]
            [blog.posts :refer [posts]]
            [cljs.core.async :refer [<! go]
            ...]))

(defn post
  [match]
  (let [{:keys [path]} (get match :parameters)
        {:keys [slug]} path
        requested-post
        (first
         (filter
          (fn [p]
            (= slug (get-in p [:meta :slug]))) posts))]
    (if (some? requested-post)
       [:div  (get requested-post :content)]]
      [:span "Post not found")])))

thheller10:01:42

is a good example of this pattern that should be avoided

p-himik10:01:27

@UKG0SELQY My bad, the error is actually visible before that. You @ the ratom way before it gets into the view - when you assign the value to def. It's executed when the resulting JS bundle is loaded - Reagent has no way of knowing that there's a ratom involved at all. As I said, put all @ on ratoms in view functions. Or in regular functions, but then call those functions only from the view functions.

p-himik10:01:10

@thheller I think there's a misunderstanding and it has nothing to do with hot code reloading. But still - how can the code piece that you quoted cause any issues, given that it's def and not defonce? Because first-post/post is in a different NS that, when changed, won't cause the namespace of posts to be reloaded?

thinking-face 3
thheller10:01:59

a -> b -> c when you make changes in c then only b is automatically updated as well but not a. so if a is holding a reference of b that is holding a reference of c then a will become stale after an update in c

p-himik10:01:55

And there's no feasible way to circumvent this, is there?

p-himik10:01:11

I mean, automatically, without having to wrap everything in lambdas and var stuff.

Grigory Shepelev10:01:52

The current situation is it's only shown on code changed and shadow-cljs reloaded. It's kinda works on recompiling only.

Grigory Shepelev10:01:04

That's the current state of the problem.

thheller10:01:37

well you can use :devtools {:reload-strategy :full} but that will make every reload slower

👍 3
Grigory Shepelev10:01:40

Nope. Again I don't want to change my reload strategy. This piece don't works as expected even when code is compiled and served with another server.

Grigory Shepelev10:01:26

The problem seems-like to be described by @U2FRKM4TW

thheller10:01:24

from what I can tell you are putting the reitit match data into an atom

p-himik10:01:29

Yes, I kinda hijacked this thread to ask thheller about a way to fix a hypothetical code reloading issue. Your problem has nothing to do with that. As soon as you move that @ inside a view, it will be working properly.

thheller10:01:36

that match data contains a direct reference to a view function

thheller10:01:47

you update the code but the match still contains the old view function

thheller10:01:10

tada you have a stuck hot-reload but I'm not entirely sure this is actually what is happening

p-himik10:01:13

Nope, that's not the described problem. Read the end of the very first code block in the OP.

Grigory Shepelev10:01:23

I don't understand what should I fix right now.

p-himik10:01:58

@UKG0SELQY You have to change quite a lot - you cannot just inline the value of @d like that. You have to call it when the view is rendered, not before that.

p-himik10:01:50

Here's something that's similar to what you're doing:

(def a (reagent/atom 0))

(def b (inc @a))

(defn view []
  [:span b])
It will not work because b is set when the JS is evaluated. Instead, something like this needs to happen:
(def a (reagent/atom 0))

(defn b []
  (inc @a))

(defn view []
  [:span (b)])

p-himik10:01:21

Now that @ is called only when the view is rendered, not before. And it makes it possible for Reagent to start tracking that ratom for changes.

p-himik11:01:49

But what thheller has said will likely bite you later, so you might want to follow his advice as well.

Grigory Shepelev11:01:30

Thanks a lot. I don't know the workaround yet, but I will most likely share it in this theead.

Grigory Shepelev11:01:44

I've got it. Changed the view to:

[:div 
 ((get requested-post :content))]
;; note the double parens
and the post itself to:
(def post 
  {:meta {:slug "first-post"}
  ;; UPDATE DOESN'T HAPPEND HERE!
   :content (fn [] [:span (str @d)])})

Grigory Shepelev11:01:04

Applying directly what you've said. Thanks/

👍 3
Shantanu Kumar10:01:41

Can anybody suggest a replacement for goog.dom.query that was removed from Google Closure? I am trying to fix up some old code that uses query.

thheller11:01:39

(js/document.querySelectorAll ".foo")

Shantanu Kumar11:01:13

Dos it also cover things like ".foo li label"? @thheller

thheller11:01:05

yep, pretty much all the same queries (and more) are supported

thheller11:01:39

it just became a browser standard (a long time ago, so even old browsers support it)

clyfe11:01:11

When using https://clojurescript.org/guides/webpack, with assets https://webpack.js.org/guides/asset-management/#loading-css, is there a way to import './style.css'; from cljs files? Mind you, the goal here is to load 3rd party css.

p-himik11:01:31

Just a guess - you can probably do that with the js* special symbol.

clyfe11:01:01

I saw a colleague use this js* once, but I found no docs for it since. Any tips?

clyfe12:01:17

nvm, found (js* "alert('xyz')")

p-himik12:01:05

Yeah, I usually just look at the sources - cljs/core.cljs or cljs/core.cljc.

clyfe12:01:18

Whines: "Can't import outside a module". I'll just make a special pack.js where I add all the css, and add it to webpack emit.

jtkdvlp14:01:12

Hi there 🙂, it seems to me I found a bug in cljs core.async (`clj` works well). Do you guys agree with me? How to report it? The issue:

(cljs.core.async/go
  (println "XXX vorher")     
  (let [old 1
      new „my-new"]
   (println "XXX nacher")
   (println [old new])))

=> „XXX vorher"
=> „XXX nacher"
=> TypeError TypeError: 1.call is not a function
Changing the new symbol to xnew works:
(cljs.core.async/go
  (println "XXX vorher")     
  (let [old 1
      xnew „my-new"]
   (println "XXX nacher")
   (println [old xnew])))

=> „XXX vorher"
=> „XXX nacher"
=> [1 „my-new"]
The following snippet of macroexpanding shows the error I mean. The new for instantiating cljs.core/PersistentVector got resolved with the value I bound to new
...
(clojure.core/let
 [inst_35971
 (println "XXX nacher")
  
 inst_35972
 (. cljs.core/PersistentVector -EMPTY-NODE)
  
 inst_35973
 (js* "[{},{}]" 1 "my-new")
  
 inst_35974
 ("my-new"
  cljs.core/PersistentVector
  nil
  2
  5
  inst_35972
  inst_35973
  nil)
  
 inst_35975
 (println inst_35974)
 state_35977
 …]
…))
So do not use new as symbol! :-$

p-himik16:01:28

Looks like a bug to me. The right process to report such things is outlined in the first section here: https://clojure.org/dev/creating_tickets

👍 3
teachtyler20:01:31

has anyone tried to spin up shadow-cljs with 2 build ids where there is :main electron :ui reagent and then a server.cljs file for express-node?

thheller21:01:05

whats the issue? i often have 2 or more builds running

teachtyler21:01:39

so i actually have 3 builds lol, kinda got it to work by calling my main server within the :main electron file, but this didn't provide HMR because it's just 2 and I was trying to make a 3rd one specifically for express

teachtyler21:01:04

reading it back doesn't even make sense 😅 I'll push this to github real quick where it was sort of working

teachtyler18:02:40

I've just been running shadow-cljs watch main ui and then launching the express server from the repl, but i can already tell this is just hacky

teachtyler20:01:56

having issues with express, I might need to rethink my plan

Oliver George20:01:02

Hands up if you've completed the clojure survey

👍 16
58
knubie21:01:46

I'm seeing some unexpected results when benchmarking transducers: I would expect this: (into [] (comp (map inc) (filter even?)) (take 100000 (repeatedly #(rand-int 2)))) to be faster than this: (->> (take 100000 (repeatedly #(rand-int 2))) (map inc) (filter even?)) But the transducer version is actually much slower (~30ms vs. 1.5ms)

clyfe21:01:24

Extra into there, how about without it?

dpsutton21:01:03

this is laziness vs realizing a large vector here

🎯 3
dpsutton21:01:21

the second is doing almost no work. the first form is doing all of the work and constructing a vector around the results to boot

Alex Miller (Clojure team)21:01:01

wrap a doall around the second to get apples to apples

knubie21:01:09

ah, got it

knubie21:01:15

results make more sense now

knubie21:01:18

thanks guys