Fork me on GitHub
#clojurescript
<
2021-05-07
>
introom13:05:43

Hi. What’s the recommended way to do logging in clojurescript?

👍 2
Scott Meyers15:05:46

When working with Reagent + Google Maps API, I've created a "Polyline" form 2 component, saving a reference of the shape (google.maps.Polyline) in an atom within the component. When Figwheel re-renders after a change, the lines disapear on the Map, but I can confirm they still exist. Does anyone have any advice for working with shapes in google maps w/ Reagent? I want to generate Polylines for each item in an array stored in my global app state. I want to then select one of them and have it styled slightly differently (stroke color).

Scott Meyers16:05:25

I think this has something to do with defonce , since I'm storing state in a component w/ let , it is lost when Fighweel reloads. I need to figure out a way to retain a reference to each shape drawn on the map that will persist.

valtteri16:05:59

How does the code look like?

valtteri16:05:47

I mean, it’s easier to help if you post relevant chunks of code here 🙂

Scott Meyers17:05:26

(defn polyline
  "Creates a Polyline on the Map"
  [path gmap]
  (let [ref (r/atom nil)]
    (fn [_ _ is-selected]
      ; if the polyline hasn't been created, create it
      (when (nil? @ref)
        (reset! ref (js/google.maps.Polyline.
           (clj->js {:path path
                     :geodesic true
                     :strokeColor "black"
                     :strokeWeight 6
                     :map gmap})))
      )
      ; if the polyline has been created and toggled
      ; set the appropriate strokeColor
      (when (some? @ref)
        (if (and is-selected (some? @ref))
          (.setOptions @ref (clj->js {:strokeColor "blue"}))
          (.setOptions @ref (clj->js {:strokeColor "black"})))
        )
      nil
      )))

Scott Meyers17:05:09

In my App component, we iterate over each item that would render a Polyline:

(doall (for [activity (:activities @app-state)]
               ^{:key (util/get-activity-time activity)}
               [maptools/activity
                activity
                (:gmap @app-state)
                (util/is-selected? (:selected-activity @app-state) activity)
                ]))]]))

valtteri18:05:44

How are you rendering the google map? Does that happen inside or outside React render tree? My guess is that on figwheel reload your whole google map gets initialised again

valtteri18:05:37

(assuming you’re not using some react wrapper for google maps)

Scott Meyers20:05:22

Yeah, I think that's the issue as well. I have a component for the Google Map, which looks like this:

Scott Meyers20:05:40

(defn google-map [app-state set-error]
  (let [api-key (subs (-> js/document .-location .-search) 1)
        center (clj->js {"lat" 40.730610
                         "lng" -73.935242})
        loader (google.maps.plugins.loader.Loader.
                (clj->js {:apiKey api-key
                          :version "weekly"}))]
    (fn []
      (.addEventListener
       js/window
       "DOMContentLoaded"
       (-> (.load loader)
           (.then (fn []
                    (swap! app-state assoc :gmap (google.maps.Map.
                                                  (. js/document (getElementById "map"))
                                                  (clj->js
                                                   {:center center
                                                    :zoom 8
                                                    :fullscreenControl false
                                                    :clickableIcons false
                                                    :disableDoubleClickZoom true})))))
           (.catch #(set-error "Unable to load Google Maps"))))
      [:div {:id "map"}])))

Scott Meyers20:05:21

I can confirm that it re-renders each time I save a change in the project directory.

valtteri07:05:07

Yep. Google Maps is highly stateful and dealing with stateful js components is a bit painful. However this strategy has worked for me well https://github.com/day8/re-frame/blob/master/docs/Using-Stateful-JS-Components.md

valtteri07:05:29

Google maps happens to be an example there. 🙂

Karol Wójcik16:05:54

Are there any alternatives to lumo which could support nodejs libraries?

Karol Wójcik07:05:29

I'm looking for something that I can embedded in AWS Lambda runtime.

thheller08:05:54

embed what? just use a random node library or cljs-eval?

Karol Wójcik10:05:47

@U05224H0W I would like to have the same capabilities as lumo for Clojurescript or ts-node for Typescript. I would like to just pack Clojurescript sources and node_modules in zip and run shadow-cljs bootstrapped-run entrypoint.cljs instead of compiling Clojurescript-> Javascript beforehand.

thheller10:05:58

I don't know what you mean by "same capabilities". why use bootstrapped if you can just precompile it normally?

Karol Wójcik12:05:10

For AWS Lambda it's convenient to have a bootstrapped Clojurescript, so you could change sources and see immediate change on Lambda.

Michaël Salihi20:05:48

Dymanic import import('foo').then(module => console.log(module)); seems now supported by the last Google Closure compiler: https://github.com/google/closure-compiler/issues/2770#issuecomment-834744931

p-himik20:05:42

You can already do the same thing in CLJS with code splitting.

Michaël Salihi21:05:32

Thanks. Rather, I saw the use to import another file dynamically like in these examples: https://inertiajs.com/pages#default-layouts But hey, this goes a bit against namespaces so I don't think so...

👍 2
p-himik21:05:48

The JS example can be adapted just fine to work with namespaces.

p-himik21:05:03

At least, it seems to me so.

thheller08:05:29

the dynamic+dynamic import style isn't supported by the closure-compiler and likely will never be, webpack also has issues with it.

thheller08:05:46

so import(someVariable).then ... will always be a problem for bundlers when they are supposed to bundle stuff

thheller08:05:30

it is fine to do at runtime when you don't expect the bundler to bundler stuff but instead import already bundled stuff like code-split modules

thheller08:05:42

webpack has a partial interpreter to figure out some dynamic imports but it is fairly limited and usually requires a bunch of build configuration

p-himik08:05:24

@U05224H0W Your example tackles truly dynamic imports whereas cljs.loader/load requires for the imported thing to be a predefined module. Are there some other differences that are important?

thheller08:05:52

you'd use the dynamic-import instead of cljs.loader (in case of an :esm build). so no cljs.loader included at all, just purely build-in import

thheller08:05:17

the import variant that can be rewritten I have not tried yet. not sure what the closure-compiler turns it into or how it needs to be configured

Michaël Salihi12:05:49

Thanks both for these replies and useful informations...very interresting. 👍

Michaël Salihi20:05:58

Is it something interresting for CLJS in the futur?

joshmiller20:05:22

I’m having an issue with eval in Clojurescript. My use case is that I’m importing a bunch of rules as s-expressions and executing them against the current state of the app. I have a function called present? that is kind of like (complement nil?) but for JS input fields, i.e. it checks to see if a field’s value is "" or js/NaN, that kind of thing. When I eval (present? 1), I get true. When I eval (or (present? 1)), I get true. When I eval (or (present? 1) true) I get nil.

joshmiller21:05:02

Does anyone have any ideas what I might be doing wrong?

p-himik21:05:44

Just as an idea - you can try supplying a custom logging js-eval to see what actually gets evaluated. Maybe it it will shed some light.

joshmiller22:05:41

Good idea, I’ll give that a shot.

joshmiller22:05:19

My or with branches seems to not be returning anything, here’s the source it generates:

joshmiller22:05:23

var or__43529__auto___44 = cljs.js.get_fn( 97 ).call(null,(1));
if(cljs.core.truth_(or__43529__auto___44)){
} else {
}

joshmiller22:05:53

Ok, taking any of my stuff out of it, it seems like something is not right: (eval (empty-state) '(or 1 2 3) {:eval js-eval*} :value) results in nil and the source it generates is

var or__43739__auto___26 = (1);
if(cljs.core.truth_(or__43739__auto___26)){
} else {
var or__43739__auto___27__$1 = (2);
if(cljs.core.truth_(or__43739__auto___27__$1)){
} else {
}
}

joshmiller22:05:47

Looking at the docs for eval, I figured it out (kind of). It takes an option :context that defaults to :statement. I need to use :expr. I’m a little unclear on what each of these does, but it generates the correct code and works.

p-himik22:05:36

A statement is something purely imperative - it has no value. An expression is anything that has a value. In CLJ(S) world, everything is an expression. In JS world, sometimes something has to be a statement to work.

👍 2
joshmiller22:05:52

That makes sense in retrospect. I have played around with the code each context generates and you can see the difference.

p-himik22:05:48

At the same time, it's interesting how (present? 1) still returns a value when using :statement.

joshmiller23:05:19

I guess that when it’s not embedded in a conditional, it doesn’t matter whether it’s side-effecting or whatever, so it just calls it and you get what JS gives you.

p-himik23:05:11

Yeah, but it should not have a return there if it's a statement. Otherwise, it could break some things. But I've never dealt with eval before, so I can be completely wrong.

joshmiller22:05:53

Ok, taking any of my stuff out of it, it seems like something is not right: (eval (empty-state) '(or 1 2 3) {:eval js-eval*} :value) results in nil and the source it generates is

var or__43739__auto___26 = (1);
if(cljs.core.truth_(or__43739__auto___26)){
} else {
var or__43739__auto___27__$1 = (2);
if(cljs.core.truth_(or__43739__auto___27__$1)){
} else {
}
}

joshmiller22:05:47

Looking at the docs for eval, I figured it out (kind of). It takes an option :context that defaults to :statement. I need to use :expr. I’m a little unclear on what each of these does, but it generates the correct code and works.