Fork me on GitHub
#clojurescript
<
2021-10-01
>
didibus04:10:18

For people who do a lot of JS/CLJS, how often in JS do things throw something that is not an Error ? To be specific, if you say wanted to model errors in core.async, so that when an error happens, you put it on the channel, but you'd want when reading the channel to know that the value represent an error. In Clojure, I can just check the type to be a Throwable and I know it was something thrown. But in JS it seems that it could be that its just an error code as an int or some error message as a string, so I wouldn't really be able to know the value from the channel was thrown and is thus an error. Which makes me wonder if I need to somehow always wrap things thrown in an Error myself and put that on the channel, or if I need to model my own error like a map: {:status error :reason value} or something?

chaos09:10:55

What I've found useful when working with channels (or even functions that never supposed to throw) is to always use a map as value with keys such as :result and :error, and then destruct the value after taking it out from the channel, e.g. something like: (let [{:keys [result error]} (<! channel)] (if error ;; error ;; else handle result))

didibus00:10:35

I see, so ya in JS it's better to wrap things to know the difference between error and success then?

Lycheese08:10:49

Hi, when using the shadow-cljs repl a lot of websocket debug messages get logged (one line every 5 seconds): 2021-10-01 10:50:12,825 [XNIO-1 I/O-3] DEBUG io.undertow.websockets.core.request - UT025003: Decoding WebSocket Frame with opCode 1 Since this makes my repl history very difficult to read I have been searching for ways to disable this. I am using sente for websocket functionality. Has anyone had this problem before and knows how to fix it?

p-himik08:10:51

It sounds like your logging setup is responsible. Figure out what io.undertow.websockets.core.request uses for logging, find out where to configure it, and redirect those messages to a file or just disable them.

Lycheese08:10:25

Any tips where/how to start looking? I checked all my explicit logging calls and none of them could produce that message.

p-himik09:10:15

Undertow documentation and source code. :)

Lycheese09:10:16

Thank you very much 🙂 Now on to figuring out how to configure it

p-himik09:10:12

I'm using Logback and seems like its configuration is respected - I get the same messages as you, but in my case they're redirected to the file I have specified.

Lycheese09:10:34

Oh, that is even part of Luminus. Goes to show that I still don't know most of the pieces…😅 Thank you very much for your help! I think I'll be able to figure out the rest.

👍 3
Lycheese10:10:28

Is there known problem with let-binding atoms inside a for-loop? I have the following code but clicking the button does not do anything:

(let [facilities {:name "test facility"
                 :city "some city"
                 :state "some state"
                 :zip "XXXXXXX"
                 :country "some country"}]
  (for [{:keys [name city state zip country]} facilities]
    (let [visible? (reagent.core/atom true)]
      [:div.card
       [:header.card-header
        [:p.card-header-title name]
        [:button.card-header-icon {:on-click #(swap! visible? not)}
         [:span.icon>i.fas.fa-angle-down]]]
       (when @visible?
         [:div.card-content
          [:span.locality city]
          [:br]
          [:span.region state]
          [:br]
          [:span.postal-code zip]
          [:br]
          [:span.country-name country]])])))

p-himik10:10:28

The issue is the code itself - you recreate the atom on each evaluation of the for's body.

p-himik10:10:02

And given that you're probably using Reagent, you should read about form-2 components and reagent.core/with-let. Those are the things you should be using to preserve state between renders.

Lycheese10:10:58

Ah Because the for-loop isn't executed just once on page load but repeatedly:man-facepalming: I'll read up on form-2 components and with-let, thank you. Incidentally is there a simple way in re-frame to do this (it seems overkill to make a new event just for this)?

p-himik10:10:27

"To do this" - what exactly?

p-himik10:10:07

Ah, to expand a card. Yeah, you just don't use re-frame machinery for that, unless you treat the card state as a part of your app's state.

Lycheese10:10:48

Thank you for your help

Lycheese11:10:58

Hmmm… With with-let I'm getting an error about the same with-let being used multiple times within the same reactive context (still renders fine but the buttons don't work). When trying to wrap the component into an inner fn it returns a warning that functions are not valid react childs (component doesn't render at all; maybe because the component I'm trying to create already is inside a form-2 component? I would've thought they could be nested though) Would I need to restructure the entire document for this to work?

p-himik11:10:51

What's the code that gives those warnings/errors?

Lycheese11:10:45

I put the body of the for-loop into its own function now:

(defn facility-card [name city state zip country]
  (r/with-let [visible? (r/atom true)]
    ;; (fn [name city state zip country])
    [:div.card
     [:header.card-header
      [:p.card-header-title name]
      [:button.card-header-icon {:on-click (swap! visible? not)}
       [:span.icon>i.fas.fa-angle-down]]]
     (when @visible?
       [:div.card-content
        [:span.locality city]
        [:br]
        [:span.region state]
        [:br]
        [:span.postal-code zip]
        [:br]
        [:span.country-name country]])]))
This is the with-let variant. For the form-2 I replace with-let with plain let and wrap everything under the call with the commented fn. I am probably stupid and overlooked/misunderstood something.

Lycheese11:10:36

Yep, I'm stupid I called the component with () instead of []

p-himik11:10:44

And then how did you use facility-card?

p-himik11:10:57

Ah, right - just got to your last message. :)

Lycheese11:10:45

Works just fine now

Lycheese11:10:55

I need more coffee…

Lycheese11:10:37

Once again, thank you for your help (at this point I should have a keyboard macro for that sentence)

😄 2
zendevil.eth13:10:17

I have these three imports:

["@mui/material/styles" :refer [createTheme ThemeProvider]]
    ["@mui/material/colors" :refer [red purple]]
    ["@mui/material" :refer [Box Link]]
but I’m seeing these errors and the page isn’t loading. No idea why.
app.js:1560 An error occurred when loading module$node_modules$$mui$material$node$CircularProgress$CircularProgress.js
SyntaxError: Identifier '_system' has already been declared
    at eval (<anonymous>)
    at Object.goog.globalEval (app.js:495)
    at Object.env.evalLoad

p-himik13:10:30

Are you using shadow-cljs?

p-himik13:10:23

Add :output-feature-set :es5 to the JS compiler options.

zendevil.eth13:10:04

i already have es6

zendevil.eth13:10:13

should I go back to es5

p-himik13:10:15

@U05224H0W It's around the fifth person with this error that I see. Do you think it would be possible to somehow autodetect it? Or, maybe, just suggest :output-feature-set :es5 if the default :es6 fails?

thheller18:10:56

I'd rather figure out what the actual problem is. can't really autodetect runtime failures, or at least can't think of a reliable way

thheller18:10:21

just one of those issues that isn't easy to figure out. already spent a couple hours on it but didn't get anywhere

👍 1
thheller09:10:16

looked into it a bit more. the correct fix is setting :js-options {:entry-keys ["module" "browser" "main"]} apparently. otherwise you end up with "node" code which appears to be broken

p-himik09:10:14

Oh, so even though :es5 seems to work on the surface, it might actually be broken without those :entry-keys?

thheller09:10:32

no, don't set :output-feature-set at all

thheller09:10:06

I still don't have a clue why it breaks with es6 and not es5 but for some reason circularRotateKeyframe = keyframes is turned into (0, _system.keyframes)

thheller09:10:42

that (0, ...) trick is usually used to call stuff in the global scope. I don't know why they do that, must be something babel is doing

thheller09:10:48

to me it looks broken

thheller09:10:06

guessing it has something to do with strict mode

p-himik09:10:33

Gotcha. Thanks!

thheller09:10:13

I might need to revisit the decision of keeping commonjs the default at some point

Benjamin Solum00:06:28

Greetings from the future! Sorry to bump a dead thread but I'm running into this same issue (only my error is: SyntaxError: Identifier 'global' has already been declared. I've tried the suggestion above to no avail. Switching to an :es5 feature set solves the issue completely. Any ideas? Seems to still be an issues with ESM imports.

:output-feature-set :es8
:js-options         {:entry-keys ["module" "browser" "main"]}

thheller06:06:17

if you have a reproducible case I can look at it

zendevil.eth13:10:59

I have the following:

(defn navbar []
  [:> Box {:bg "primary"}
   [:> Link {:href "/"}
    [:> Heading {:fontFamily "Epilogue"} "Humboi"]]
   [:> Box {:height "40px"}
    [:> Link 
       {:href "/i-have-a-creation"}
      "I have a creation"]]])
but I see this error:
Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Check the render method of `humboi.core.navbar`.
any ideas why?

zendevil.eth13:10:33

navbar used here:

(defn page []
  (if-let [page @s/common-page]
    [:> ThemeProvider {:theme (theme)}
      [:> Box {:direction :column}
       [navbar]
       [:> Box {:height "100px"}]
       [:> Box {:height "100vh"}
        [page]]]]))

Apple13:10:00

I smell circular invocation here...

Apple13:10:15

Or not...

dgb2313:10:19

Delete all the lines in your component and recompile with every added line. Only takes a couple of seconds. Then at least you know where the bug is hiding.

dgb2313:10:37

or rather comment them out 😄

dgb2313:10:00

Assuming you did import all the used components, such as heading, link etc.

Quentin Le Guennec15:10:06

Hello, what's the idiomatic way (if any) to extend function F or procotol P for object O, with the default implementation of F in the function definition of F', the extended version of F?

p-himik15:10:20

I don't think I fully understand what you mean but there are extent-protocol, extend-type, and you should be able to extend a protocol to support the type js/Object as the default implementation.

Quentin Le Guennec15:10:22

Maybe an example would be more explicit.

(extend-protocol Slack
  Quentin
  (ask [this]
    (do-something-with (previous-version-of-ask this))))

p-himik15:10:55

Just name the default implementation something else and make it not depend on any protocol, like default-ask.

Quentin Le Guennec15:10:34

yeah that'll work, I'll do this if it's the only way

Quentin Le Guennec15:10:53

there's probably no smarter way right

p-himik15:10:06

This is the smart way, because it's the simplest one. :) This is not the first time this question is asked, although usually it's asked in #clojure.

Quentin Le Guennec15:10:26

okay, thanks a lot for yout time then!

p-himik15:10:45

In more generic words: don't do protocol dispatch when you know the exact implementation you need.

Quentin Le Guennec15:10:09

yeah I should've probably asked in #clojure, I thought that maybe there would be some clojurescript specific magic

Quentin Le Guennec15:10:51

yeah no I'd say it's a natural pattern, super in Java is an example of this

p-himik15:10:21

It's a natural patterns for inheritance. Protocols are a different thing.

Quentin Le Guennec15:10:00

yeah, you're probably right