reagent

2023-04-13T13:57:01.712249Z

I have some constants currently in EDN (industry categories, codes, this sort of stuff). Can I load an EDN file as in making it part of the bundle (it's not too large, I don't want to load it via XHR)? Or do I have to wrap it in (def xxx [...]) and make it a CLJ file? Would prefer EDN if I can keep it like that.

p-himik 2023-04-13T14:01:00.089909Z

Sure - you can write a single-line macro that reads that EDN file and embeds its contents into a CLJS file, right where that macro is used.

1
2023-04-13T14:01:41.725259Z

Good idea.

2023-04-13T15:06:48.855289Z

(defmacro inline-resource [path]
    (slurp path))
And then:
(defonce activities (edn/read-string (inline-resource "attachments/data/industry.edn")))
Useful ref: https://clojureverse.org/t/best-practices-for-importing-raw-text-files-into-clojurescript-projects/2569/10 In case it's useful to anyone.

p-himik 2023-04-13T15:08:16.950529Z

I would move read-string into the macro. Unless you have some specific data readers that only work in CLJS.

👍 1
thheller 2023-04-13T19:32:24.096139Z

also see https://clojureverse.org/t/using-none-code-resources-in-cljs-builds/3745

👍 1
thheller 2023-04-13T19:32:48.960699Z

depending on the size it might be better to use read-string on the client side. raw edn text compresses better than their JS code counterparts

2023-04-13T21:57:42.267959Z

@thheller oh cool, good to know!

Matt DeAngelis 2023-04-13T19:11:58.528319Z

What would folks suggest for creating forms in a shadow-cljs app with Reagent? I've been creating elements myself and linking them through ratom state, but it has been difficult to coordinate different elements with one another, keep state coordinated with the values in dropdown menus and the like, and perform validation. It doesn't help, I'm sure, that I'm a web app newbie and know no Javascript. I checked out reagent-forms but it looks like it isn't compatible with the new render method in React. Is re-frame my only real option? It looks complicated to my eyes but maybe I should bite the bullet.

liebs 2023-04-13T20:21:29.599179Z

Theres the fork library that's reagent and reframe compatible

liebs 2023-04-13T20:22:12.410129Z

I personally like re-frame alot, it took me some time to wrap my head around it at a conceptual level but now that I "get it" I think it's awesome

liebs 2023-04-13T20:25:03.855869Z

https://github.com/luciodale/fork

Matt DeAngelis 2023-04-13T20:42:55.948319Z

Thank you Ben, I will check out these options. I have looked at re-frame and found the approach interesting, so it sounds like maybe that is the way to go. But I will also check out Fork, thank you for the link 🙂

👍🏻 1
liebs 2023-04-13T20:52:35.033249Z

NP!

hifumi123 2023-04-13T22:36:57.191229Z

I'm using react-hook-form with a custom resolver for Malli schemas and it works great

hifumi123 2023-04-13T22:39:09.054039Z

Somebody here uploaded a small example project of integrating react-hook-form with Malli; it's not a lot of code necessary and react-hook-form does an extremely good job of minimizing re-renders and managing state for error messages, form validation, etc

Sam Ritchie 2023-04-14T00:35:47.911289Z

http://inside-out.matt.is/ another option

Matt DeAngelis 2023-04-14T10:46:43.364219Z

Thanks to you both. I was checking out Malli in another thread yesterday and it seemed very cool. I hadn't seen Inside-Out yet but it sure looks easy, which is nice. I will do my homework on each of these options and play around a bit.

Matt DeAngelis 2023-04-14T10:58:53.042809Z

For reference, it looks like the example for Malli with react-hook-form is here: https://github.com/dvingo/malli-react-hook-form

hifumi123 2023-04-14T11:23:30.250549Z

That’s right. The key function in that project is the use-malli-resolver hook. It will return a function that you can then pass to react-hook-form’s useForm hook. Afterwards, you get validation and human readable error messages for free. And react-hook-form is very flexible in how often to validate, re-render, etc. In general, it does its best to avoid re-rendering at all and use uncontrolled state, but it offers a wrapper for controlled components, and it’s super trivial to wrap controlled components (you just have to know where to place the returned on-change, on-blur, ref, name, value. props; but you dont need to use all of them; the more you can use, the better it can avoid re-rendering though and provide nice experiences like focusing on form controls with an error when the user attempts submitting a form).

Matt DeAngelis 2023-04-14T11:51:53.549779Z

This is helpful. I dove into the dvingo example a little this morning and I'll need to spend more time with it to figure out how it will interact (if at all) with what I already have. There are so many options, I'm trying to plug things in a little at a time and make them workable so I can understand them better.

Rupert (Sevva/All Street) 2023-04-17T06:14:50.005159Z

re-frame will introduce a lot of boilerplate code and often quite tight coupling, so only go that route if its really worth it for you. Depending on your usecase, are some options: • If you don't need the state to be handled client side, then you can submit the forms to your backend (the default form behaviour) - this will give you all the form state without having to write any client side code to manage or serialise it. • You can use https://github.com/jkk/formative which is a declarative library for forms and can be rendered in clojure and clojurescript that manages form state. This library includes form validation. • Build your own form state management utility functions (e.g. backed by reagent atom) • You can use #integrant in clojurescript to do dependency injection and manage the state of your reagent code. • You can move to another client side react wrapper like #helix or #uix. - but this won't automatically solve managing form state.

hifumi123 2023-04-17T06:24:48.667439Z

IMO re-frame only feels like boilerplate if you’re making small-scale things like TodoMVC. For anything more interesting than that, it’s a very nice solution and feels roughly equivalent to Zustand from the JS world. I would also be careful saying “without having to write any client side code to manage or serialise it” regarding uncontrolled forms submitted to backend. input[type=number] gives you a string that still needs to be serialized into a number on the backend, it also doesn’t prevent people from inputting invalid things (e.g. letters), etc. Lastly, formative is cool but it seems to require buying in to their own specification language and doesn’t seem to elaborate on human-readable errors or schemas where valid options of a key depend on the value of some other key (e.g. only display and allow Canadian provinces when a :country key is set to Canada). IME using helix with react-hook-form has left me satisfied with form validation in CLJS, at least compared to libraries like Fork and form-validator-cljs.

hifumi123 2023-04-17T06:26:17.946369Z

I do agree that re-frame isn’t the only way to handle state, though. If someone goes down the Helix or UIX v2 route, they can go a far way by simply using hooks + context; you just lose all of the debugging goodness that re-frame offers, and context updates re-render all of their children, which may be an issue for performance

Rupert (Sevva/All Street) 2023-04-17T06:39:21.427059Z

I just find the re-frame boilerplate has very little benefit - teams I have worked with often end up with tightly coupled frontends and the clojure community has very few shareable re-frame (not reagent) component libraries because re-frame components are not suited to sharing the way most teams use them. An unscientific piece of 'anecdotal data' on re-frame boilerplate: we rewrote a re-frame app into uix and reduced line count by over half. > gives you a string that still needs to be serialized into a number on the backend, Yes, on the backend you still need to handle form elements and possibly coerce from string into other data types. The thing is you usually have to write all of this on the backend anyway - since validation on the user side can't be trusted. So doing it all on the backend just eliminates duplicating the validation code. Yes, formative is only for use cases where you don't mind giving up some control for the declarative syntax. Having said that, it is pluggable so you can create your own form elements that do whatever you need. Certainly not suggesting it for most projects, but there are some which can benefit from it.

hifumi123 2023-04-17T06:41:46.559959Z

Anecdotally, I’m finding a re-frame app being rewritten with Helix to be fairly straightforward because I’m able to re-use my subscriptions with a single custom hook (that calls useSyncExternalStore), and events, too. I guess it depends on what exactly subscriptions return. For instance, I expect subscriptions returning Hiccup to be non-portable and tightly couple oneself to re-frame and make it hard to re-use much of anything

👍 1
hifumi123 2023-04-17T06:42:58.379079Z

Out of curiosity, are you using Uix v1 or Uix v2? And are you simply using context or some in-house state management solution?

Rupert (Sevva/All Street) 2023-04-17T07:22:57.160539Z

We're on UIX V1 - since we like that it uses hiccup datastructures instead of macros. We use a custom wrapper around context in a few places - but most of our state management is done by integrant wiring together the frontend (e.g. a state hook can be injected into the 3 compnents that need it, an API handle can be injected where it is needed etc). A lot of our latest development has been done fully server side rendered hiccup frontends with bits of htmx and _hyperscript where needed. They're basically indistinguishable from client side rendered UIs to our users but much simpler for us to code up and have the added benefit of better SEO/ integration into the main website.

👍 1