Fork me on GitHub
#clojurescript
<
2022-10-17
>
Hankstenberg05:10:30

Hi guys, I have a question about reagent and react interop. Given a component like this:

<NavLink label="With icon" icon={<IconHome2/>} />
I can render the NavLink like this: [:> Navlink { :label "With icon" .. but how do I get the embedded component in there? Neither [:> Navlink { :label "With icon" :icon [:> IconHome2]}] nor a reference to a function that calls "adapt-react-class" seem to work.

1
thheller05:10:58

it requires a react element, so :icon (r/as-element [:> IconHome2]), r being reagent.core

1
🙌 1
Lone Ranger12:10:44

This crucial piece of wisdom should be starred/pinned somewhere

Hankstenberg05:10:30

Awesome, thanks 🙂

Hankstenberg11:10:12

Hm... do you think it makes sense to drop re-frame for react hooks? Or only for certain use cases? One disadvantage of hooks seems to be that it becomes almost impossible to check out the app state with the repl.

p-himik12:10:19

I have never used hooks and so far don't plan to. But in any case, you can use re-frame along with hooks - they aren't mutually exclusive.

Lone Ranger12:10:28

Is that new? You use the hooks in cljs or in react? I thought there was some issue with making hooks outside the element (aka in the hiccup)

Hankstenberg12:10:59

Yea, it's not new and sure, hooks and the re-frame store can perfectly coexist. I'm just wondering what should be used when. It seems nowadays you're running into hooks sooner or later if you use the react ecosystem and they are a nice and lightweight way to deal with side-effects. Just wanted to discuss some pros and cons.

p-himik12:10:47

FWIW, I don't know of any advantages of hooks that would be worth it at least in my applications. > they are a nice and lightweight way to deal with side-effects Don't know what you mean by "lightweight", but compared to re-frame's effects, hooks: • Are harder to test • Complect rendering with side-effects • Aren't traceable by default

Lone Ranger13:10:39

I agree... reagent/atoms >>> hooks

Lone Ranger13:10:09

but so many off-the-shelf components are written with hooks these days that it's really good to have the option for interoperability if necessary

p-himik13:10:31

You do have that option, and any such component you can just wrap and forget that there are hooks there under the hood.

❤️ 1
Lone Ranger13:10:42

Well sounds like no reason to ever stop using reagent yay

lilactown14:10:50

hooks manage side effects, but they don't do any side effects themselves. they instruct the React runtime to coordinate with whatever side effect you're trying to do. this has some advantages. when using hooks exclusively, rendering a component is idempotent, which means that React can feel free to start rendering your component off screen, interrupt itself with a higher priority update, and then resume rendering where it left off without fear of rendering elements with state inconsistent with the rest of the screen. if state changes outside of the React runtime that components depend on, then React can't detect whether that state has changed, so it has to start over with rendering. the above property is what enables a lot of the new features that React is developing. See https://reactjs.org/blog/2022/06/15/react-labs-what-we-have-been-working-on-june-2022.html

lilactown15:10:45

the way that Reagent components currently work, rendering isn't idempotent; when you deref an atom, if you were to rerun that component with the same inputs again, it could render different results. it also uses the deref during render to setup watchers that will rerender the component when the atom changes. this is also unsafe, and could lead to memory leaks if React were to begin rendering a component tree and abort before completing. this isn't necessarily a problem with the fundamental model of reagent, but is an issue with the current implementation. I think reagent adopting hooks more would help with this, since then reagent could let React coordinate rerenders and focus on more important things like hiccup and signal graph stuff.

lilactown15:10:00

the app state being hard to inspect is a tooling problem. the React fiber tree is accessible at run time, which you could use to inspect the state of any component rendered on the page. it is a hard UX problem to figure out how to inspect that at the REPL

lilactown15:10:32

although, most projects that use re-frame use something other than the REPL to inspect the app-db, such as re-frisk or re-frame-10x. these provide nice GUIs to view the data. this is essentially what React DevTools provide. Again, we have a tooling problem though, because CLJS data structures don't print correctly in it

lilactown15:10:24

liberally, we should use whatever tools get the job done. reagent as it is today, without using hooks, gets the job done in many cases. but I think if we want to implement the same out of the box UX that idiomatic React can build in the future, we will need to adopt hooks

p-himik15:10:07

> hooks manage side effects, but they don't do any side effects themselves Is this workflow as open as the re-frame's one with "action->event->effect", where you can observe and alter any part at any moment with some provisions? > the app state being hard to inspect is a tooling problem The global and immutable nature of re-frame's app-db allows for relatively easy undo/redo and persistence. Are React hooks similar in this regard?

lilactown15:10:12

the observability of re-frame's event framework has more to do with its use of data over closures. React uses closures by default because they are performant and very powerful. you could build something more data centric (and thus observable from a tooling perspective) on top of hooks

p-himik15:10:08

Of course. But seems like that "something more data centric" is not there, yet. :) Thanks!

lilactown15:10:34

undo and redo is very hard IME. doing it in re-frame is also a trial; do you store the entire app-db in a data structure outside of the app-db, or do you store only collections of state inside of the re-frame app-db that are set up for undoing & redoing? how do you handle branching? these same questions lie in React. IME undo/redo for the entire app state is not desirable, so you end up implementing it for only certain states, which then it's roughly the same problem doing it in re-frame vs doing it using React state. more important is the use of immutable data, which both React and re-frame encourage

lilactown15:10:42

I think there are a few data centric hook frameworks for ClojureScript, IIRC. none of them have caught on it seems

dvingo15:10:50

hooks also provide very clean abstractions for DOM interactions. this library has lots of useful ones, e.g. https://ahooks.js.org/hooks/use-long-press the state management hooks in that lib aren't as useful for cljs, but being able to leverage reusable hooks like this for DOM manipulation means not having to reinvent that stuff yourself in whichever cljs library equivalent

p-himik15:10:53

So far, I've had a pretty smooth experience in re-frame with undo/redo, given that I: • Use https://github.com/day8/re-frame-undo for a global undo/redo to restore from a critical error (consecutive service-level data changes are lumped together) • Limit the current undo/redo stack to a specific editing context

Hankstenberg07:10:47

Thanks for these insights @U4YGF4NGM! I also think that it's subtle. There are definitely use cases for hooks as well as central state store. I mean right now I'm using mantine and they have an incredible collection of highly usefuly hooks (https://mantine.dev/hooks/use-queue/). My impression so far is that hooks are useful for certain technical abstractions while the business logic should be handled centrally in the re-frame DB.

Lone Ranger13:10:00

Thanks @U4YGF4NGM , great points!

jlfischer16:10:25

Hey, does anyone know of a good ClojureScript library for internationalization?

cjohansen17:10:54

We built this data-driven thing, which allows you to late bind translations: https://github.com/cjohansen/m1p it's more of a toolkit that can be used for i18n than a batteries included i18n library though

🎉 1
valtteri18:10:53

There’s also https://github.com/tonsky/tongue with which I’ve had success in the past

magnars06:10:37

The cool thing about the late binding of translations in m1p is that you can move knowledge about dictionaries and tr -functions out of lots of code, and handle i18n in one place further out.

Fredrik Andersson16:10:47

Is there a clojure.test/function? for cljs?

p-himik16:10:28

No, probably because clojure.test/function? relies on resolve. But if you don't need that part of its functionality, you can just use fn? instead.