Papercuts with Reagent & React integration


#1

So I’m finally using Reagent in anger to see how some of our existing UIs could look in this fantastic new world. I’m a huge fan of using clojure atoms for state management, and of course hiccup is awesome to easily manipulate HTML snippets.

Things however get muddier when you want to integrate a few key React components, esp. when those components need to do ref forwarding and so on.

In my particular case, I’m working with React-Bootstrap and in particular all the pop-up/overlay/tooltip components there. They’re all based on popper.js so they rely on having access to the underlying node so that the overlay/tooltip can be positioned correctly on-screen.

In this case, I have to remember to constantly wrap my nice hiccup components with (reagent/as-element), which I don’t understand very well (does it return a new instance of an element overtime it’s called?), or sometimes I need to use a render function which also requires converting #js props into reagent-friendly props and then back again with as-element.

While it’s not such a big deal, it does get a little annoying as you have to always remember the correct arcana for getting this right, otherwise you don’t get a nice error message at all.

The second thing is that a) Reagent does async rendering and b) if you use reagent atoms, the React lifecycle functions don’t get called, so any wrapper components that could rely on componentDidUpdate in order to call back out to the DOM and reposition the popup, can’t. Which is a little-bit solvable but the workaround seems really brittle.

I’m not sure if this is something that Reagent could solve, or perhaps I just need to wrap all this mess into a set of “bridge” components that I could then use worry-free from Reagent land. Or perhaps other Reagent-like libraries exists that have less of an impedance mismatch with the React world? I’ve seen mentions of those but would be nice to know how they handle this kind of problem.


#2

I’ve found that when trying to leverage a lot of the React libraries out there, it was quite painful using Reagent. This was the exact kind of use case that drove me to write hx.

It gives you Reagent-like hiccup parsing and helpers for component creation and React hooks.

Components created with the defnc macro are actual React components that take a props object and return React elements, and the hiccup parser expects either keywords or actual React elements to be used. This helps a lot in dealing with many of the paper cuts you’re talking about when doing a lot of React interop.

Here’s an example using material-ui, which is similar to the React-Bootstrap library you are trying to use.

Here’s an example of how to do state management using React hooks instead of RAtoms.

Give it a shot, I think it might help with your use case. Feel free to hit me up here, in slack, or open an issue on Github if you run into any problems or have any questions.


#3

Agreed!
A couple minor points:

Isn’t that also true of React nowadays? (We use preact, which I believe has had that for a long time)

At least on a quick, lazy experiment (wrapping a r/create-class with a r/reactify-component and calling it with :>) they were correctly called (tested did-mount, will-unmount, did-update).
Does this only happen with “real” React components?


#4

Thanks for releasing hx, I was looking into it yesterday. Would you consider it stable enough to base a production application on? I guess I mean about API, bugs, omissions, scary unforeseen dead-ends etc…


#5

Silly question - can I mix and match with reagent and hx? So I could write low level components with hx and drop them in a reagent codebase? Or would that defeat the purpose?


#6

I would consider things like the hiccup parsing, defnc and the hx.hooks namespace stable. I’ve been using it personally to build punk, an application meant to be similar to REBL. So far it has served my uses with that application well.

The thing I’m not super confident in (and don’t have much documentation or examples of) is the defcomponent macro API. It is similar to Reagent’s create-class function in that it gives you direct access to the React class API, but it requires quite a bit of JS interop to use. If it’s something that people want to use often, I would give it more love and attention. The use cases you need it for that you can’t use a functional component are very few now that React Hooks are here.


#7

You can mix and match them, but I’m not sure how much it would buy you. hx components are regular React components, so you’ll have to use Reagent’s interop tools to use any hx components in your Reagent components (and vice-versa).

I suppose you might be able to wrap some of the gnarlier ref-using / render-prop-using components with hx as a component that takes simpler props, and then use Reagent’s React interop to convert them to reagent components.

You can also use RAtoms with the <-deref hook in hx.hooks as a way to share state between hx portions of your app and Reagent portions of your app.

@aisamu RE: async rendering. React hasn’t enabled concurrent mode yet but will sometime this year. It’s scheduler is a bit more advanced than Reagent’s, e.g. it treats certain events at different priorities, like changes in reaction to text input onChange are rendered immediately. This routes around a lot of bugs that have popped up in other implementations like Reagent, Rum, etc.


#8

Sounds like I need to read up on react hooks. Would it be ok if I keep asking questions on this? Don’t want to take up too much of your time :blush:


#9

Yes! I will probably head to bed soon but will answer them as I can :smiley:


#10

A follow up for anyone who is interested – I’m having a great time working with hx. Avoiding the mental overhead of differentiating between reagent components and React components is nice, devtools work great, and hooks is really a game changer. I haven’t missed reagent’s ratoms at all.

Disclaimer: I’ve no idea how a very big app that uses more advanced reagent features like cursors etc might be adapted over to hx/hooks.


#11

How about re-frame with hx? Does that work?


#12

I think it would be awesome to have re-frame work with React + Hooks, since it is obviously such a ubiquitous and powerful framework for state management in the CLJS ecosystem. For many people, it is one of the top reasons to use reagent.

Re-frame is unfortunately coupled to reagent in a few ways that makes me hesitate to recommend using hx with it. I actually have a personal fork of re-frame with all the reagent bits removed, but it doesn’t have subscriptions, which are a big part of re-frame.

You can include re-frame in your hx application and use hooks with the RAtoms returned by re-frame subscriptions; either using hx’s <-deref, or a hook that works directly with reagent reactions, example: https://github.com/Lokeh/hooks-demo/blob/master/src/hooks_demo/hooks.cljs#L41 (Note: I have not tested this hook myself).

There are some interactions with reagent component, RAtoms/Reactions, and re-frame that I don’t understand completely, which might cause subtle issues when using them inside of a vanilla React component. There might be some features that don’t work as expected in re-frame, e.g. flush-dom on events.

If someone were to try it, and document their findings, that in itself would be useful. I’m not sure I’ll have time in the near future to do this.


#13

Maybe citrus is a better fit for hx?


#14

Just saying: after talking with @lilactown I plan to create a similar very thin layer on top of React, with support of JVM SSR (like Rum). I agree with @orestis, React hooks are a game changer. I also plan to integrate with a CLJS state management like Citrus, since Citrus is coupled to Rum at all ( and I contribute to citrus so I know it very well). Expect something some time this year.


#15

Interesting! Would the state management be plain React-compatible or would it be specific to the library?

I’m curious to see what comes out of your efforts! Do you have an idea about the differences with hx, reagent and the new wrapper?


#16

Plain React-compatible, the goal of this will be to be a “Just React in Clojure”, with hooks for reaching into atoms and Clojure state management techniques.

With hx: I think not that much, at least last time I checked hx I liked several things, even if it is a bit more “Clojury” than “Reacty” than I’d like, but really the main difference will be support of SSR on the JVM.

With reagent: I’ve been bitten too much by Clojure wrappers around React because they often make design decisions that deviate from React itself. As Dan Abramov said recently, we must think about React as a UI runtime. As a game engine. Better embrace it to fully take advantage of it. So one should expect nothing more than just React disguised with a Clojure syntax. My goal is to allow 100% interoperability between both (in both ways) and to have the same problems than React has (which I consider a strength!). If we have the same problems, we’ll also have the same solutions. Embrace the platform, as we say in Clojure :slight_smile:


#17

Hey @DjebbZ we have started an #hx channel in Slack and also trying to collect our thoughts about wrapping the Hooks API in some Clojure-friendly API (or at least, trying to avoid pitfalls or issues from immutable data structures, equality semantics, concurrent react etc etc).

@lilactown was so kind to gather the current state of affairs over at https://github.com/Lokeh/hx/issues/41 , every feedback is welcome :slight_smile:


#18

Thanks for pointing out this discussion.