I'm walking over a data structure that contains JS "classes" like #object[Boolean]
and #object[String]
. How can I detect if an object is such a "class"? In regular Clojure I'd just use class?
, but that's not available in cljs.
cljs.user=> (type [])
cljs.core/PersistentVector
cljs.user=> (type #js [])
#object[Array]
cljs.user=>
cljs.user=> (instance? js/String (js/String.))
true
cljs.user=> (instance? js/String "")
false
JS is awesome
I don't think that solves my problem though
How do I tell if something is #object[Boolean]
or #object[String]
or #object[Number]
etc, as compared to a concrete instance of one of those classes, or to a Clojure object?
Can you describe precisely what that "etc" means?
And what's the problem you're trying to solve in the first place?
I have a data structure that contains a random assortment of java classes and regular values/objects, or the corresponding objects in javascript (i.e. what we get via (class foo)
), and I need to tell the classes apart from the concrete values
on the JVM this is trivial, because I can just use class?
but I'm not sure how to do it in JS
Do you consider functions to be classes?
if it's what's returned from class
, then yes
in practice I figure that this should be doable, because e.g. prn
seems to be able to handle it fine
Well, you're out of luck, it seems. In JS functions are those "classes".
16:48:03.106 class X {};
16:48:03.108 undefined
16:48:07.154 X
16:48:07.156 λ[]
16:48:10.762 typeof X
16:48:10.764 "function"
16:48:41.318 function Y() {};
16:48:41.321 undefined
16:48:46.524 y = new Y();
16:48:46.526 Y {}
If your values cannot be functions, then you can just filter with fn?
.
ew
:D
they can be functions too though
Welp. Unless you have a predefined set of "classes", I don't think you can solve that problem.
Or a predefined set of proper functions.
I guess I'll just try to enumerate all the classes I can think of and do an equality check
and cross my fingers that no other "classes" pop up
There are only String
, Boolean
and Number
oh, cool
@nilern What do you mean? What would be a common description for those?
Primitive wrappers
I might be wrong, but I didn't get an impression that @wombawomba was talking specifically about those. I'm willing to be corrected though. :)
Woops there are more than three https://developer.mozilla.org/en-US/docs/Glossary/Primitive#primitive_wrapper_objects_in_javascript
bigint and symbol are quite new, thus my omissions
What is the community recommendation on web component selection which supports react and is usable within clojurescript, preferably with the interopt abstracted away. I've tried both react-bootstrap and re-com with some success, but wanted a more informed feedback on direction
I'm pretty sure the answer is basically the same as it would be in the JS community in general. Interop is not that much of a problem, but of course depends on the environment in which you'll be using web components. E.g. Reagent does most of the work for you.
There’s also Helix, if you prefer a more direct React interop without much of state management.
right now, im appreciating the re-frame model for state management, but am ignorant on best practices for the widgets/components
Widgets/components as in “building your own” or “using existing” ones?
i'm hoping to leverage existing libraries
There are no best practices here, just like in JS. Some UI component libraries work for some people, some work for others. E.g. I chose Material UI specifically for two reasons - I like its documentation and its number input component is sensible (can parse scientific format).
Got it; You can just used them directly. I personally use my own judgement about the trade off between value provided and ability to change that component widget. Another question to ask is: “Is that component/widget at the right level of abstraction for the problem I’m solving?”
For example, I’ve seen people building collaboration software and basing a big part of their product on a text editor widget; When you hit the limits/bugs of that widget, you’re stuck with a pile of complex code that nobody understands.
On the other hand, picking a nice animation library that provides some fundamental animation abstractions can be a good choice.
I prefer CSS-only widgets, less fighting
CSS-only? Most React components/widgets I’ve seen don’t fall in that category.
Examples?
Well just CSS -> no React
Got it 🙂 Yeah, haven’t done any development outside of React(Native) for a long time; can’t speak to that.
It's been a while, I've used Bootstrap and Bulma but not a huge fan of those either
I wanted to try https://getmdl.io/ at some time
I see. I wonder how much value something like Bootstrap provides nowadays given that flex-box is widely supported.
re-com is leveraging flex
however, it usses css styling from bootstrap
In my mind widgets and layout are different feature sets
Taking a quick look at re-com… @franco.gasperino have you used it?
i've tried a bit of my internal prototype with re-com & re-frame. It works well, but lacks some widgets (navigation bars) that would be helpful
i dont have any JS experience, so the removal of another tech stack simply for interopt is appreciated by me
I don’t have much production JS experience either; Most of modern React-specific JS is relatively understandable, at least on the surface level; Digging into complex libraries/widgets is another story.
are there any landmines which are known to the cljs community, specifically with regard to "avoid component library X", that I should know?
In any case one should make sure that the widget library is a net benefit. I have heard things like Material UI are risky in that regard (inflexibility, issues with Reagent etc.)
https://github.com/reagent-project/reagent/blob/master/examples/material-ui/src/example/core.cljs
@franco.gasperino I don’t think there’s a list that says GRAB (generally recognized as bad) 😄
alright, i appreciate the feedback from the 3 of you
Perhaps I should make one! 😝
generally = as by me
ill stick with re-com for now. it mostly stays out of my way. the core of the SPA will be https://github.com/projectstorm/react-diagrams , while the re-com will handle the UI navigation bits
@franco.gasperino I'm so happy you posted that link to react-diagrams, hadn't heard of it and it's exactly what I was considering having to DIY implement a bit down the line 😵
I don't know how easy it will be to integrate. I've done a reagent/adapt-react-class call or two, but beyond that ...
On the surface all of the React libraries will work via Reagent interop :>
and hopefully you are using shadow-cljs to do the npm imports.
Beneath the surface there is a lot to unpack. Picking a UI library is not as simple as it was when only Bootstrap was around. Since then so many things happen and frontend community experimented in so many ways that it’s hard to keep up.
In general there are a couple of things you can use, I’ll categorize them in the following way but it’s just my point of view, don’t consider this as a ultimate:
React + CSS; these libraries require npm install of a library and adding a CSS file to your <head />.
• https://material-ui.com
• https://ant.design/docs/react/introduce
• https://react-bootstrap.github.io
• https://blueprintjs.com
• https://react.semantic-ui.com
Tradeoffs:
• It’s CSS so it’s global thing and after some time you will be wondering can I remove this class or is it used anywhere?
React Components + CSS-in-JS; these libraries require only npm install and CSS is generated by JS/CLJS, this solves the Cons of the React + CSS, because your style lives together with the component and not in the global namespace (CSS file).
• https://evergreen.segment.com
• https://rebassjs.org
• All other libraries based on https://styled-components.com or https://emotion.sh/docs/introduction
The way this works is that styles are dynamically generated at request time. Some libraries generate styles at build time so you would have to check.
Tradeoffs:
• Dynamically generated styles, which means you throw away caching.
• Styled components need additional nesting of classes and they generate additional wrappers for your React elements which bloats your DOM tree and slows down the React diffing since the more your have in your tree the longer it takes; compare Google Chrome Dev Tools Elements vs Components tabs.
• You bundle size will be slightly bigger because each component now has CSS definitions, on the other hand you don’t have CSS file.
• Writing CSS in JS is sometimes painful, they rely heavily on template literals and if you don’t use shadow-cljs which proves some additional facilities to deal with that, you will suffer sometimes.
• You need to name almost every single component, and naming is hard.
Reagent + Atomic CSS; this is somehow new way to work with CSS, you have utility classes, sort of abstraction over CSS syntax, and as you can imagine your initial bundle size is huge since most of the libraries have new names for CSS. To work around this there is a https://postcss.org plugin that does tree-shaking for your CSS (removes unused styles from your CSS, think Google Closure compiler for CSS). There is also a new approach in tailwind-css where you have a JIT compiler to generate classes that are used in your files — check out this setup https://github.com/jacekschae/shadow-cljs-tailwindcss if you are interested how to get this going.
https://tachyons.io
https://basscss.com
http://buildwithbeard.com
https://turretcss.com
https://tailwindcss.com
Tradeoffs:
• You end up with pretty messy CSS classes, some people don’t mind, some people hate it.
Hope that helps.
Perhaps because of this: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/String
“The `String` constructor is used to create a new `String` object. When called instead as a function, it performs type conversion to a https://developer.mozilla.org/en-US/docs/Glossary/String, which is usually more useful.”
Thank you, @jacek.schae
on the topic of best practices, what is the cljs equiv of slurp?
Depends on the cljs runtime, fs/readFileSync for node
is there a generic approach to a browser-based runtime?
generally browsers do not have access to the file system
in this use-case, i'm mocking out a json message which would be arriving to the runtime via an async call. I'm looking for a way to create the message locally for development.
(.resolve js/Promise data)
would be how I would mock an async call
I dont need to mock the async bit, i need to mock the actual content. (-> (slurp) (js/JSON.parse) (js->clj) -> ... throw onto channel)
it's the slurp equiv i'm attempting to replicate for mocking
If the content you are expecting from slurp is a json string, you could mock slurp with (constantly "<json string>")
makes sense. was looking for a way to maintain the string outside of the code for this case. I'm running a number of mock content inputs against a schema validation, and they're getting to be overbearing to define all as values in code
(def mock-element
{:id (str (random-uuid))
:name "mock-element-1"
:description "This is a mock"
:repo "configured-repo-1"
:type :input
:widget :square
:config {}})
(defn edn->json [content]
(-> content
stringify-keys
clj->js
(js/JSON.stringify)))
(edn->json mock-element)
would like to move mock-element out to local file for testing
(granted what i pasted is the inverse of the question, but illustrative nonetheless)
(defn json->edn [content]
(-> content
(js/JSON.parse)
js->clj
keywordize-keys))
this isn't possible to do at runtime because browsers, where your code is running, do not have access to the file system
dang
there is a way to do this in shadow-cljs, but I don't really know that it's any better then keeping the EDN in a CLJS file?
https://clojureverse.org/t/using-none-code-resources-in-cljs-builds/3745
The FileReader class is strictly for node (@djblue suggestion) or in-memory payloads from forms or blob arrays, then?
right
You could also do this with a clj macro, https://github.com/djblue/portal/blob/master/src/portal/resources.cljc I how I do it
right, you can read in the file at compile time but the naive version will not reload on changes to those files. this can get annoying and complex if you change those files at all often.
the reloading problem is solved by the shadow-cljs helper in the link I posted
still, if you are set on moving these outside of code that is the best approach
btw, portal is very very cool 🙂
thanks for the input