Fork me on GitHub
#shadow-cljs
<
2019-05-06
>
steveb8n02:05:08

Q: I’m struggling with interop to https://github.com/FezVrasta/react-popper. Does anyone have sample code for this? I have it working for an older version but I want to use the latest and I can’t get the react refs working properly in reagent

lilactown02:05:36

render props are pretty difficult to use in reagent

lilactown02:05:17

this worked for me:

(ns popper-example.core
  (:require [reagent.core :as r]
             ["react-popper" :as rp]
             [applied-science.js-interop :as j]))

(defn popper-example []
  [:> rp/Manager
   [:> rp/Reference
    (fn [props]
      (let [ref (j/get props :ref)]
        (r/as-element [:button {:type "button" :ref ref}
                       "Reference element"])))]
   [:> rp/Popper {:placement "right"}
    (fn [props]
      (let [{:keys [ref style placement arrowProps]} (j/lookup props)]
        (r/as-element [:div {:ref ref :style style :data-placement placement}
                       "Popper element"
                       [:div {:ref (j/get arrowProps :ref)
                              :style (j/get arrowProps :style)}]])))]])

(r/render [popper-example] (.getElementById js/document "app"))

🙏 4
steveb8n03:05:24

Worked first time. Thanks a lot!

steveb8n03:05:43

@U4YGF4NGM any chance you’d be willing to show me how to style it (using Emotion I presume). That’s not obvious either

steveb8n03:05:07

I worked out emotion styling for react-select but this is very different

steveb8n03:05:20

I also found https://github.com/bwalex/verktyg but I’d prefer to avoid another dep if it’s not too much interop

lilactown04:05:32

Seems straight forward. Pass in classes / styles to the elements you render in the render prop

steveb8n04:05:34

yes it should be but I don’t do js interop much so the incantation is not obvious to me

steveb8n04:05:07

I’ll keep trying

steveb8n04:05:12

ok. got it. I see what you mean. sorry for the noise

lilactown04:05:24

No problem! duckie

teawaterwire10:05:34

I always forget why we need the global and local installation of shadow-cljs?

$ npm install --save-dev shadow-cljs
$ npm install -g shadow-cljs

thheller11:05:37

@teawaterwire you don't need the global install

thheller11:05:56

you only need it if you want to use shadow-cljs ... instead of npx shadow-cljs ...

🙌 4
teawaterwire12:05:12

makes sense, thanks!

thheller11:05:27

you need it in the project to ensure that other dependencies are installed in the project

thheller11:05:53

also to make your project reproducible and not rely on some global install

hlolli12:05:49

@thheller I read somewhere that you tried getting react-native hot reloading to work without expo, was it a struggle, you think it's possible to implement?

Stefan12:05:49

@hlolli Maybe you read my posts here about it? I had trouble with it because I also wanted to use “React Native Navigation” (from Wix). With Thomas’ help I was able to get it working, see https://github.com/svdo/CLJSReactNativeNavigation. With normal react-native stuff it should just work with shadow-cljs, no difficulties that I know of.

hlolli12:05:01

@stefan.van.den.oord nice! There have been no difficulties so for, I can turn on "Live Reload" on the iPhone simulator, but I can't see if the websockets are connecting.

Stefan12:05:42

You actually don’t need to turn “live reload” or “hot reloading” on in the simulator; shadow-cljs uses a different mechanism for live reloading.

hlolli12:05:34

ok good to know, it's a new territory for me, I'll pop up some questions after digging trough your code 🙂 thanks again

Stefan12:05:17

Sure but remember that my code is needlessly complicated if you’re not using React Native Navigation…

thheller13:05:37

@hlolli the "complicated" bit was only that react-native wants a react component class registered as the root

thheller13:05:58

and that doesn't mix too well with hot-reloading because we can't swap it after

hlolli14:05:22

do you enable something in the simulator, because I'm using this code character by character, and a nested compinent isn't updating, except via CMD+R

thheller14:05:43

define nested component?

hlolli14:05:34

A react component that's not root but a child

hlolli14:05:02

so child component is probably a better lingo?

thheller14:05:03

(defn start
  {:dev/after-load true}
  []
  (render-root (r/as-element [root])))

thheller14:05:07

you have this right?

hlolli14:05:12

yes, exactly this

thheller14:05:30

(defn root []
  [:> rn/View {:style (.-container styles)}
   [:> rn/Text {:style (.-title styles)} "Hello!"]
   [:> rn/Image {:source splash-img :style {:width 200 :height 200}}]])

thheller14:05:38

and here you render something new?

thheller14:05:06

it should update just fine ... with the same caveats react usually has of course

hlolli14:05:23

ah, ok, maybe it's because my root is routes

hlolli14:05:07

there were same caveats with the react-native-router, going to look better at the repo which @stefan.van.den.oord sent me

thheller14:05:09

in this design it MUST be a function. it cannot be anything else

thheller14:05:28

yeah the router thing had multiple roots which we had to work arround

hlolli14:05:29

yes it's a function

hlolli14:05:46

ok, I'll noise down for now 🙂 looking into this

lilactown13:05:29

I wonder if that will get better soon. I know the RN team is working on a re-architecture and supporting more modern React development practices

thheller13:05:05

I'm pretty much done with react so I don't care anymore 😛

thheller13:05:52

the hack works well enough though so it doesn't really matter

lilactown13:05:25

lol! what are you eschewing React for now these days?

thheller13:05:46

well .. in true lisp fashion I wrote my own thing of course 😉

thheller13:05:43

I was never quite happy with react but told myself that the value of react is in the ecosystem not react itself

thheller13:05:53

that entire argument went out the window with hooks

thheller13:05:39

well ... its changing everything and is even for unfriendly for CLJS than components were

thheller13:05:03

can't change equality semantics of the inputs check so forever doomed to work around it

thheller13:05:41

can't use hooks within components either so have to replace everything with something new

thheller13:05:57

when I'm doing that anyway I can get rid of react entirely in the process

thheller13:05:20

of course thats not something you should ever do if you actually care about 3rd party react components

thheller13:05:35

I don't ... 🙂

lilactown13:05:55

I see your points

thheller13:05:19

of course my thing might fail completely and I may just go back to React ...

thheller13:05:44

but I started building a new app and tried to do so with hooks and I don't like the experience one bit

lilactown13:05:06

that’s the fun bit. I think there’s plenty of places to explore outside of the React model

lilactown13:05:39

Svelte, Ember, Phoenix LiveView are all things I’m watching. it would be interesting to explore those in a CLJ(S) way.

lilactown13:05:46

especially LiveView

thheller13:05:51

I have sort of a hybrid of svelte and react

thheller14:05:06

performance close to svelte but programming model of react

thheller14:05:59

I'll probably write a blog post about the ideas behind it

lilactown14:05:59

if we could leverage more static analysis of CLJS code it would be pretty cool to see the kind of compile-time tricks we could play with it

thheller14:05:07

I think they are worth talking about

lilactown14:05:09

that would be really cool!

thheller14:05:48

yeah macros are a super power here

lilactown14:05:20

right. we have the tool chain built into the language. seems like we should be able to leverage that far better than the JS ecosystem

thheller14:05:21

but the real killer is protocols 🙂

lilactown14:05:57

how do protocols help in this case?

thheller14:05:45

I'll explain in the blog post

lilactown14:05:03

what a tease 😂

thheller14:05:20

you can do magic stuff if your main constraint isn't create ReactElement objects 🙂

lilactown14:05:58

yes, ReactElements are annoying because you can’t extend them without extending js/Object

lilactown14:05:32

no idea if that’s what you’re talking about, but I just remember running into issues when I first started building hx since I wanted to build it using only protocols. funnily, the lack of OO-ness in React internals bites us

lilactown14:05:04

plus I wanted to do weird things like datafy and nav React elements, React components

thheller14:05:15

protocols with react make no sense yeah

lilactown14:05:53

I’m also curious, the dislike of the Hooks experience: was that using hx? anything in particular you didn’t like other than the equality semantics?

thheller14:05:19

no my own thing I did the benchmarks for a week ago or so

lilactown14:05:39

ah, I thought that was this new thing

thheller14:05:22

well I have been building "my own thing" for 5 years now

thheller14:05:37

never anything worth talking about and typically just got deleted 😉

thheller14:05:32

but this one seems different enough and so far is more promising than all other attempts combined

lilactown14:05:57

I’m very interested now

thheller14:05:52

don't hold your breath .. these experiments don't have a high survival rate 😉

lilactown14:05:38

hahaha. well, I’m just interested in trying to get outside of my React box in general

thheller14:05:44

but the protocol thing seems worth talking about as a concept for react alternatives in CLJS

💯 4
lilactown14:05:54

I think I’m becoming myopic in my view of web dev lately

lilactown14:05:31

I kinda realized after debating with yogthos for the billionth time about React that maybe I need to chill and take a wider view of what’s out there

thheller14:05:18

(defprotocol IConstruct
  (as-managed [this env]))

(defprotocol IManageNodes
  (dom-insert [this parent anchor])
  (dom-first [this]))

(defprotocol IUpdatable
  (supports? [this next])
  (dom-sync! [this next]))

thheller14:05:53

yeah the CLJS world sometimes looks like there is only React

thheller14:05:34

which I used to think is a good thing .. now not so much anymore

thheller14:05:56

everyone used to make fun of angular when v1 wasn't compatible with v2

thheller14:05:07

but react with hooks isn't much better when you can't use them in class components

lilactown14:05:25

eh I think it’s much better than v1 to v2

lilactown14:05:41

angular was a whole-app thing. class components and react hooks can live side-by-side in the project

thheller14:05:49

yeah not really

lilactown14:05:53

you just have to choose

lilactown14:05:08

when you create an individual component

thheller14:05:08

say you move your state management stuff into a hook

thheller14:05:23

now you have to rewrite all your class components that used the old way

lilactown14:05:57

hm. I would move it into context, and then each component can consume it in whatever way is best

thheller14:05:18

right .. context sssssssuuuuuuuuuuuuuuuuucks with class components 😛

lilactown14:05:21

ex. for class components, you might use a context Consumer component

lilactown14:05:50

😅 yeah I hate the function-as-child pattern, especially compared to hooks

thheller14:05:14

yeah context with hooks is good

lilactown14:05:16

an HoC is a thing to help clean up the boilerplate

lilactown14:05:58

I see your point that it makes it so that you essentially split your development paths: one API for class components, one API for hooks

thheller14:05:06

HoC ... yeah where was this site that listed the react trees of doom? 😛

lilactown14:05:33

but it’s still much better than angular v1 to v2

lilactown14:05:11

I was moving to React after angular v1.4 and saw the explosion in my rear-view mirror 😎

thheller14:05:13

yeah maybe I exaggerated a bit ... still to me it felt like writing in a new fragmework

thheller14:05:28

didn't feel like the previous react anymore

thheller14:05:52

also simple things like (react/useState {:hello "world"}) deeply offend me

thheller14:05:11

since the map is just gonna be thrown away after the first render

thheller14:05:24

so I did (def init-state {:hello "world"}) and (react/useState init-state)

thheller14:05:43

when then started to look like a component again 😛

thheller14:05:02

dunno ... the problem I have with hooks is really just about the inputs array

lilactown14:05:04

😄 yes. there are pros and cons to returning a closure (like reagent does) vs creating the closure implicitly like React does

thheller14:05:14

and how it doesn't fit with cljs types

thheller14:05:13

maybe we get access to the lower level APIs some day .. that would be nice

lilactown14:05:23

I actually think it’s fine

thheller14:05:27

but ... I wouldn't count on it so I'm out

lilactown14:05:47

the inputs array was a red herring (IMO) I chased for like a couple weeks

lilactown14:05:09

fundamentally you have the exact same problems using any aggregate JS value

lilactown14:05:23

I ended up creating a useValue hook that handles clojure equality for things like deps, but in practice I haven’t used it. https://github.com/Lokeh/hx/blob/master/src/hx/hooks.cljs#L108

thheller14:05:34

the nice thing is since we have hicchup as a basis switching between frameworks doesn't mean throwing away your entire HTML 🙂

thheller14:05:16

[:div.card {:on-click [::inc]} foo]

thheller14:05:40

I also wanted declarative events ... 😉

lilactown14:05:57

that looks slick

lilactown14:05:08

but yeah, IMO the inputs deps are the wrong place to optimize. I think we want to optimize by memoizing components and efficient state updates. if you maintain referential identity (e.g. aren’t passing in literals every render) then you can take advantage of the tools the same way JS React does

thheller14:05:39

quite the opposite. if you do it wrong everything will be slow.

thheller14:05:13

so you have to think about the from the start

lilactown14:05:23

I think that React kind of helps you do it right by convention, no?

lilactown14:05:06

for example, you rarely are doing something like (useEffect foo [{:bar "baz"}])

thheller14:05:32

(let [ev-handler (react/useMemo #(handle-some-event clojure-data) #js [clojure-data])] ...)

thheller14:05:34

doesn't work

lilactown14:05:55

it does though

thheller14:05:40

it does only if the clojure-data is actually identical?

thheller14:05:52

eg. if its something you got from props it isn't

lilactown14:05:24

right, which is (IME) usually the case

thheller14:05:59

well it fulcro style apps it is

lilactown14:05:00

that depends. if you’re re-creating the clojure data on every render, then yes it’s not going to memoize correctly

thheller14:05:18

I'm over optimizing here .. I know that its not a big deal

thheller14:05:36

its just one of those things ...

lilactown14:05:46

in my experience, I’m usually depending on something that is in useState or a callback function

lilactown14:05:14

the former case, identical? works great. the latter case, there’s useCallback (gross but it’s a solution)

lilactown14:05:51

I think it is a big deal. these things can sink the UX of an app

lilactown14:05:06

one day your app is slow and you have to optimize 10,000 places 😬 that’s not fun

lilactown14:05:19

I think it’s kind of annoying in that it is making us all think about how to leverage immutability again

lilactown14:05:03

we had it kinda figured out with class components + sCU, and reagent and Om went a long way to covering it up (for me at least)

lilactown14:05:35

now we gotta figure it out again. and it’s kinda turning out that it’s more about following React idioms than using actual immutable data

lilactown14:05:07

I think that even if we could customize the equality, that we would find out that all the places where we were doing deep equality of clojure data would be a bottleneck as well and end up optimizing it the same way we are now 😛

lilactown14:05:18

anyway, sorry, I’ll stop ranting. I need to learn to shutup more..

thheller14:05:09

one issue is also that all library hooks are going to use JS objects

lilactown14:05:55

it’s annoying yeah.

thheller14:05:14

so just things like const props = useSpring({opacity: 1, from: {opacity: 0}}) already suck

lilactown14:05:21

what’s actually more annoying is how good mhuebert’s js-interop lib is

lilactown14:05:31

I think I’m going to throw out parts of hx and use it instead 😛

thheller14:05:46

yeah .. worth adding to core imho

lilactown14:05:10

definitely. I imagine they won’t, but holy crap is it good

thheller14:05:32

well I don't quite think the extra symbol syntax is needed

lilactown14:05:34

and it helps a lot with what you’re saying. but I agree that CLJS-native hooks are always better

thheller14:05:37

but still nice regardless

lilactown14:05:49

I mostly just appreciate the lookup