Fork me on GitHub
#reagent
<
2018-03-13
>
kishanov06:03:12

I’ve been trying to organize my thoughts around Containers in Reagent and ended up writing an article (https://medium.com/@kirill.ishanov/using-containers-with-reagent-and-re-frame-ba88c481335d) and a sample application (https://kishanov.github.io/reagent-containers-demo/#/) which showcases the concept. Would love to hear some constructive criticism 🙂

crinklywrappr16:03:50

I'm trying to use cljsjs.react-burger-menu and am getting this error: TypeError: _reactDom2.default is undefined when expanding the menu

crinklywrappr16:03:08

Has this been removed in newer versions of react, or do I need react-with-addons, or maybe something else?

crinklywrappr16:03:40

currently have [reagent "0.7.0"] and [reagent-utils "0.2.1"] as only react deps

justinlee16:03:49

@doubleagent I have never really understood cljsjs 100% which is why i don’t use it

justinlee16:03:11

but i wonder if there is a conflict with cljsjs.react-burger-menu’s declared react dependency and reagent’s 0.7.0 dependency

justinlee16:03:36

i’ve not wrapped my head around how common ancestor dependencies are supposed to be resolved in cljs

crinklywrappr17:03:54

Ah. I could try downgrading reagent to 0.5.1 and see if that helps

justinlee17:03:28

actually from some googling it seems that the problem is your code isn’t include the ‘react-dom’ package

justinlee17:03:21

supposedly reagent 0.7.0 includes react-dom 15.5.

justinlee17:03:42

you shouldn’t have to downgrade

justinlee17:03:21

actually try adding [cljsjs/react-dom "15.5.0-0"] and [cljsjs/react "15.5.0-0"] to your dependencies to see if that forces resolution to one version of react

crinklywrappr17:03:04

will i need to add :exclusions to any packages?

justinlee17:03:53

try it. i might add react and react/dom exclusions to reagent and react exclusion to the burger. again, sorry, i really just don’t understand the resolution algorithm and as far as I can tell there isn’t good documentation on it

justinlee17:03:10

one of the reasons why i prefer shadow-cljs

crinklywrappr17:03:17

same problem. just going to abandon the pretty sidebar idea.

crinklywrappr17:03:24

thanks for your help though!

justinlee17:03:02

shoot that’s a bummer. maybe someone else can help. you shouldn’t give up! 🙂

justinlee17:03:29

if you are doing a bunch of stuff with react libraries, really consider a move over to shadow-cljs. you won’t regret it

gamecubate17:03:06

Is there a way to have a reagent component function invoked whenever one of its children is mounted?

gamecubate17:03:22

Am working with form 3 so easiest way might be to handle this from the child, in its :component-will-mount lifecycle handler. In that case however, not sure how I can access my parent.

justinlee17:03:33

pass a callback function to the child

justinlee17:03:17

if you are going to do this all over the place, you might create a higher order component that does nothing but render its children in an empty container and then call the callback

gamecubate17:03:22

as in

:component-did-mount (fn post-mount-handler [this] ...)
?

justinlee17:03:31

yes like that

justinlee17:03:36

that’s the most straightforward way

gamecubate17:03:43

how would I identify my parent?

justinlee17:03:12

oh sorry. in the parent, when you create the component, pass a function as a prop

justinlee17:03:26

then in component-did-mount, invoke that prop

justinlee17:03:57

the passed function can close over whatever you need to do in the parent. the child doesn’t have to know anything except what to invoke

gamecubate17:03:35

Thanks. Let me digest. And also state an assumption, namely that a component reaches :component-did-mount only once all children components are mounted.

justinlee17:03:10

actually i don’t think that assumption is true. i think that the parent must mount before the children, but let me check

gamecubate17:03:35

Let me clarify. Am trying to use reagent for threejs (without http://aframe.io). A typical view would look like

(defn my-3d-view []
  (fn []
    [default-scene]
      [cube 1 1 1]]))

justinlee17:03:50

nope i’m wrong

gamecubate17:03:28

So then might be possible to obtain a list of one’s children once one has been mounted. In that case I could iterate and just call ThreeJS’s scene.add function.

justinlee17:03:45

are you trying to get access to a dom element from the parent?

gamecubate17:03:45

My question then becomes: how does one obtain a list of one’s children?

gamecubate17:03:58

Yes. a child, i.e., sub element

gamecubate17:03:30

I could use the DOM’s childNodes property but was wondering if reagent had some function for that.

justinlee17:03:31

there’s more than one way to do this. the way i would do it is to pass a callback to each child that adds the ref to an atom in the parent. let me write a little example

justinlee17:03:01

i wouldn’t do that. you’ll regret it at some point 🙂

gamecubate17:03:10

Feels sneaky.

gamecubate17:03:25

and risky, given shadow dom optimizations and all

justinlee17:03:15

so you know you can do this: [:div {:ref (fn [ref] (reset! *input-ref ref))}]

justinlee17:03:29

that will place the dom node into an atom called *input-ref

justinlee17:03:39

so instead of doing that, pass a prop to the child from the parent that does something like [child {:ref-cb (fn [ref] (swap! *input-refs assoc :some-identifier}]

justinlee17:03:14

then in the child do [:div {:ref (:ref-cb props)}]

justinlee17:03:31

or jsut pass it as an argument you don’t need to use a props map

justinlee17:03:09

now, once compoment-did-mount has run, you know that you’ll have a map of your children’s dom nodes

justinlee17:03:17

and the children don’t have to know

justinlee17:03:40

oh, also: you said that you were using form-3 components. you can call reagent/dom-node from within component-did-mount of your form-3 components for the same effect

justinlee17:03:47

the ref call back can still be helpful if you don’t want to return the top-level dom node, though

gamecubate17:03:08

True.

(.-parentNode (reagent/dom-node this))
would give me a child’s parent.

gamecubate17:03:23

Indeed to your last point.

gamecubate17:03:06

In the case of threejs though, every child belongs to a scene so it might not be too much of a stretch to have it know that it needs to add itself to that (parent) scene.

gamecubate17:03:37

hence, in :component-did-mount of said child

gamecubate17:03:57

however, how does one, once one has the parentNode property, call a function on it. The node is only the representation (a rendered canvas), and not the object (a ThreeJS scene) I need to add myself (as child) to. Hmmmph.

justinlee17:03:49

so you really don’t want the children to be calling a function on the parent. you want to pass a callback from the parent to the child. the callback will probably mutate an atom in the parent, but the child doesn’t know that.

gamecubate17:03:02

Need to rethink this some more.

gamecubate17:03:46

Simplest is in fact to do something from the parent when a child is added.

justinlee17:03:49

yea i don’t know the architecture of what you are doing. i will say that react doesn’t shadow canvas or svg elements, so you almost always end up doing something that feels a little hacky there.

justinlee17:03:24

in my case, all of this nonsense comes from rending a pdf to a canvas, so i feel you

gamecubate17:03:33

ThreeJS is extremely stateful so it is a hack to begin with. But thanks for your advice. I need to play with it a bit. Might be the best solution (passing callback to child components)

justinlee17:03:57

it really depends on which component needs to be doing the action

gamecubate17:03:59

We’re bot canvas victims then. 🙂

gamecubate17:03:06

I prefer the parent. Makes most sense.

gamecubate17:03:22

Need to find that method invoked in reagent when a prop (child) is added. Gotta be one.

justinlee17:03:49

so you really don’t want the children to be calling a function on the parent. you want to pass a callback from the parent to the child. the callback will probably mutate an atom in the parent, but the child doesn’t know that.

gamecubate17:03:07

Once I find how to invoke a function whenever a child component is added, I’ll just call

(swap! my-state update-in [:children] conj)
.

gamecubate17:03:31

(to your last point)

justinlee17:03:07

what do you mean “whenever a child component is added”

gamecubate18:03:27

I meant mounted. The parent component keeps internal state, a reference to the ThreeJS.Scene object it created at initialization. The, as you add children components, e.g.,

[my-3d-scene-view]
  [default-cube-view]
  [default-cube-view]]
I need to tell my parent to add each child’s underlying ThreeJS model (a ThreeJS.Mesh in this example) to its underlying ThreeJS.Scene. But, since component-did-mount is only reached once all children are mounted, I may instead use
(doseq [child (get-children this)] ...)
or something similar. Am looking at reagent’s docs right now.

gamecubate18:03:59

Must be something like that somewhere. Docs aren’t super helpful though.

justinlee18:03:37

my instinct is that having the children register themselves on mount through a callback is a more robust component model, but I think ^^ will help you. i’ve never used your library so your approach might be better.

gamecubate18:03:37

Good (docs) catch! This might be the answer for my needs. But yes, will weigh that against responsibilization of children (as they are mounted).

justinlee18:03:14

be sure to take a look at the /docs directory on github

gamecubate18:03:33

I just saw it. Yep.

justinlee18:03:42

me and a few others have recently been trying to document reagent and for now, that’s where things are

gamecubate18:03:24

What’s in /docs is actually quite good. Sure could use some visibility push on the main project page.

justinlee18:03:26

eventually i want to put things in a nicer format and link off the main page, but we haven’t settled on a tool yet. also: please consider submitting a PR if you see anything that could be buffed up. it’s super easy to submit doc PRs on github.

gamecubate18:03:07

Okay will do.

juhoteperi18:03:42

Only way for React component to get access to it's children's React component instance is through :ref function. r/children (`this.props.children`) is different thing. It returns the React elements of the children (i.e. the data structures that describe the children), or often in Reagent, in contains rest of the component arguments.

juhoteperi18:03:57

(React components usually take single properties map and zero or more childrens as arguments. Reagent components take zero or more arguments, and as implementation detail, the first one is used as React properties map and rest are the React children)

justinlee18:03:28

@juhoteperi can you not call reagent/dom-node on the child elements?

justinlee18:03:02

fwiw i agree with you that ref callbacks are the best way to go here

juhoteperi18:03:49

dom-node is from React component instance to DOM Node, and you can only get the React component with :ref

justinlee18:03:23

i only really had to understand how react works once i started using reagent 🙂

justinlee19:03:32

also @juhoteperi i think there are a bunch of reagent issues that we could close outright e.g. #5, which I’m pretty sure is irrelevant now. there are others where it isn’t clear what needs to be done or that have gone stale. I think on those we should maybe ping the reporter and close if there is no follow up (e.g. #116).

justinlee19:03:51

would it be helpful for me to just comment on some of these with a suggestion? i don’t want to create busy work

justinlee19:03:27

I guess I should ask @mikethompson and @gadfly361 for their opinions too since they both seem engaged on this stuff right now

juhoteperi19:03:46

Yeah, #5 was about text refs but they are deprecated now

justinlee19:03:01

the reason i’m asking is because i was going through the issues to see if there is information in there that could be pulled out to put in the docs, and while i was there i was thinking if i could be useful

juhoteperi19:03:09

Does someone want to take look at this: https://github.com/reagent-project/reagent/pull/352 ? I could push out alpha3 soon with this and maybe https://github.com/reagent-project/reagent/pull/351

juhoteperi19:03:17

I bumped ClojureScript compiler to test version few days ago so now CI is successfully running tests on Node + Foreign libs which was one thing that had regression on 0.8 alphas (and required fix in Cljs compiler)

juhoteperi19:03:53

I still have few problems running tests on Browser+Node modules+Advanced compilation, I'd like to fix those before release

justinlee19:03:25

will it break with an older cljs compiler? i use shadow and i don’t know what underlying version we’re on right now

juhoteperi19:03:56

Only if you target NodeJS AND use Cljsjs foreign libs (i.e. no node_modules)

juhoteperi19:03:21

Browser + Cljsjs or Node modules works, NodeJS and Node modules works, but NodeJS and Cljsjs requires new compiler

juhoteperi19:03:36

I wonder if there is any reason for 0.8 to keep React 15.6 as default at this point?

justinlee19:03:04

if it has fragment support, that doesn’t really make sense right?

juhoteperi19:03:03

Yeah. I was hoping to release 0.8 with just JS Module changes last september just when React 16 was released, but I think by now most React 16 bugs have been fixed

jmckitrick19:03:44

So @lee.justin.m I see you are really encouraging moving to shadow-cljs (if we use reagent, react, etc). Does it integrate well with leiningen builds?

justinlee19:03:49

I don’t use it with lein, but I’m pretty sure even @thheller uses it with lein. So in short I know it theoretically works with lein but I don’t know how well

justinlee19:03:00

i’m sure lein is great but i find the documentation lousy and it seems super complicated and magic. i really don’t like magic. with shadow i don’t need most of what lein has to offer me. of course, i’m targeted a javascript backend so i get why an integrated build would be desired for others

jmckitrick19:03:05

Ah, ok. We are doing front-ends, and integrated builds.

justinlee19:03:10

you should hop over to #shadow-cljs. thomas is on central european time, but he’s always there and always super happy to answer questions

jmckitrick19:03:51

Good to know, thanks!

jmckitrick19:03:13

He’s been very responsive to my questions so far, but I don’t think it fills a need I have right now.

jmckitrick19:03:36

But I’m ready for when it does, like if someone on my team doesn’t like the toolchain.

jmckitrick19:03:18

I have bigger fish to fry with the backend

justinlee20:03:34

if you have a toolchain that works, then use that. i just push it on people who are obviously doing greefield development and people who are obviously coming from javascript and anybody who is foolishly trying to use the npm-deps feature. these kind of people really want to use npm and really don’t want to futz around with externs. shadow-cljs is great for that

jmckitrick20:03:50

Good to know.

gadfly36120:03:45

@lee.justin.m help with cleaning up tickets is always welcomed!

jmckitrick20:03:36

How would you explain to a js developer the difference between re-frame and redux? The subscriptions?

juhoteperi21:03:55

I finally found a reason why test suite doesn't pass with node_modules + prod build: https://github.com/facebook/react/issues/12368. Luckily this only affects server/render-to-string/static-markup.

justinlee22:03:19

@jmckitrick I’m not a re-frame expert so actually someone in that channel might do a better job. But re-frame is quite similar to redux. Subscriptions to me seem fairly analogous to mapStateToProps. There are analogs for actions and reducers. Re-frame has more abstractions and more levels of indirection, so it includes some of the functionality that you might find in redux-thunk or saga

justinlee22:03:08

to me it seems to be more general and adds a kind of middleware like layer in the middle of handling “actions” and “reducers”

mikethompson22:03:44

@jmckitrick I've seen people describe re-frame as "redux without all the boilerplate". Then they talk about the "immutable by default" side of using clojurescript. Then they talk about how re-frame uses an "effects as data" approach. Then, finally, there's subscriptions.

mikethompson22:03:31

There are similarities but also clear differences. See https://purelyfunctional.tv/article/react-vs-re-frame/

jmckitrick22:03:51

Hmm, I thought redux bragged about effects as data too

mikethompson22:03:48

Not as far as I understand it

mikethompson22:03:02

(although my knowledge of redux is quite limited)

justinlee23:03:54

I feel like you might be using the word “effect” in a different way. The central feature of redux is that you describe a mutation using a pure-data “action” and then create a new version of the state based on that action.

justinlee23:03:48

The state transformations are always immutable, which allows redux to do shallow compares when figuring out what to update.

justinlee23:03:40

When I was learning re-frame, the biggest departure was the effect and co-effect handler system, which is very different from the reducer paradigm. This is the part of re-frame that starts to bleed over into redux’s sibling libraries that deal with async calls.

justinlee23:03:05

Oh and the interceptors. Redux has middleware too, but the interceptor stuff in re-frame is much more elaborate.

mikethompson23:03:47

re-frame's effects approach is a "subset" of the reducer paradigm.

mikethompson23:03:05

I wouldn't characterise it as "very different"

mikethompson23:03:30

In fact, there's a standard way in r-frame to end up with a reducer kind of fucntionality. Its just that re-frame allows you a way to "reduce the world", not just reduce "application state". Hence super set.

justinlee23:03:25

can’t you dispatch new events from re-frame effect handlers?

justinlee23:03:22

I guess I felt they were quite different because reducers are so simple in redux. It’s literally just a big case statement on the action type that returns a new state. In re-frame (I thought) you can cause side effects and dispatch new events and so forth (am I wrong about that?).

mikethompson23:03:36

Like I said, you can do super simple reducers in re-frame. See reg-event-db

justinlee23:03:43

I was trying to answer the question of how it compares to redux. Re-frame offers a lot more and solves problems redux does not solve by itself. And a lot of that functionality is in the effects/interceptors portion of re-frame. This is possible because effect handlers can do things that are absolutely verboten in redux reducers.

mikethompson23:03:33

No problem. I was just trying to clarify some of your info about re-frame.