Fork me on GitHub
#reagent
<
2017-06-12
>
urbank13:06:57

I'm sure this has been covered many times, but I can't find it atm. Do form-2 components need to have their arguments duplicated in the returned anonymous function?

pesterhazy13:06:42

the reason is that, if you don't do that, the render fn will only see the initial props, even if the component receives new props

pesterhazy13:06:13

it will appear to work initially, but you'll notice that the component doesn't update under certain circumstanes

borkdude13:06:37

I have the following code: https://gist.github.com/borkdude/177148ef9d57cb144e26bf2f16e4c7da The problem is that when the loading? value changes, the child component report-iframe gets mounted a second time. Why?

pesterhazy13:06:40

my advice is not to specify the props in the outer fn at all - you should usually use the props in the inner fn only

borkdude13:06:07

not with the #_ comment in place btw, then it works

urbank13:06:22

@pesterhazy oh, I didn't know that was valid to omit them in the outer function. Cool, thanks

pesterhazy13:06:28

because clojurescript is javascript, you can call a fn with the "wrong" number of args

borkdude13:06:42

note that the report-iframe component is not inside the when, so I didn’t expect this value to affect the mounting of the component

pesterhazy13:06:05

@borkdude it's easier to help if you can reproduce the problem in a klipse

borkdude13:06:28

true, I’ll see if I can get it in there

borkdude13:06:24

Note that when I call report-iframe directly (without the intermediate report component) it works without remounting

pesterhazy13:06:39

@borkdude so what's the behavior you expect, and what's the behavior you're seeing?

borkdude14:06:17

The behavior I want is that the iframe is mounted only once

pesterhazy14:06:22

@borkdude why do you want to avoid remounting the component?

borkdude14:06:14

I load some components in the iframe which can take a while to finish. When they are finished, I call the parent frame which then should display the iframe

borkdude14:06:39

I do this via the loading? value, but when it’s remounted it defeats the purpose, because it does all the work a second time

borkdude14:06:33

so why does it re-mount, that’s the question

borkdude14:06:56

Note that when I comment out

(when loading?
     [:div “Loading...“])
it works

pesterhazy14:06:27

so that's why

pesterhazy14:06:11

check this:

(defn report [loading?]
  [:div
   [:div
     (when loading?
       [:div "Loading..."])]
   [report-iframe loading?]])

pesterhazy14:06:36

the problem is that in your original, the structure of your element hierarchy changes, from [:div [:div ...] [report-iframe ...]] to [:div [report-iframe ...]], and vice versa

pesterhazy14:06:04

react can't figure out that you added/removed an element before your componenet

pesterhazy14:06:36

so it does the simpleminded thing of remounting all children

borkdude14:06:39

cool, thanks 🙂

pesterhazy14:06:58

becausre tree diffing as a general task is very complex (quadratic? or worse), react uses heuristics to diff prev-tree and curr-tree

pesterhazy14:06:50

you kind of have to structure your dom in such a way that react can see the commonalities

pesterhazy14:06:35

kudos to @viebel for klipse, which makes it so much easier to figure this stuff out together

borkdude14:06:22

Yeah and kudos to you too!

Yehonathan Sharvit17:06:31

It’s much simpler to debug tough issues when you can see the code in action @borkdude @pesterhazy

juhoteperi17:06:30

This could probably also be solved by adding React keys manually:

(defn report [loading?]
  [:div
    (when loading?
      [:div {:key "loading"} "Loading..."])
   ^{:key "report"} [report-iframe loading?]])
This way React can see that "loading" element is removed/added, and "report" is always present

borkdude17:06:22

ah I’ll try in the KLIPSE snippet

juhoteperi17:06:01

Without these manual keys, the index of children is used as key, so if loading is shown it will be element "0" and report "1", if loading is not shown report is "0"

juhoteperi17:06:09

Solution without additional wrapper elements is usually easier for styling

borkdude17:06:00

I like it better, also more robust when things get thrown around

juhoteperi17:06:25

Now that I think about this, I have probably written lots of code with similar (when loading? ..) and (when error ...)

juhoteperi17:06:05

But I guess this won't be a problem with stateless Reagent components

pesterhazy17:06:45

Is this not a problem with Reagent?

juhoteperi17:06:00

Remounting a component is usually so fast it doesn't matter, iframe is a special case

juhoteperi17:06:32

Animated components would be another case where it matters when component is added/removed

pesterhazy18:06:19

True but just randomly using keys for children seems dangerous

pesterhazy18:06:10

What this does is to essentially silence react's warning message

juhoteperi18:06:17

Setting keys manually is fine

juhoteperi18:06:00

It allows the React diffing to detect which elements are added/removed

pesterhazy19:06:29

@juhoteperi, my point is that by default React issues a warning if you don't set keys manually. If I understand Reagent's behavior correctly, it overrides this warning by issuing automatically assigned keys to children.

pesterhazy19:06:56

or is that not true?

juhoteperi19:06:59

React automatically uses index of the children for key, not Reagent

pesterhazy19:06:04

do you have a link to back that up?

pesterhazy19:06:18

not saying it's not true, but I'd love to read up on it

juhoteperi19:06:24

Or maybe more specifically, React diff algorithm uses the index of children if key is not provided

pesterhazy19:06:08

it's actually explained pretty well there

pesterhazy19:06:11

It says >>> It is important to remember that the reconciliation algorithm is an implementation detail. React could rerender the whole app on every action; the end result would be the same.

pesterhazy19:06:51

that's kind of not true... sometimes you have to rely on the fact that components persist across changes, i.e. when those components have some rich state

pesterhazy19:06:16

e.g. you don't want to just re-mount a <video> tag ... you would lose the buffer, playback position etc

pesterhazy19:06:57

so there are time where making sure that a component stays moment is more than just a performance optimization

pesterhazy19:06:44

but yeah that supports your interpretation I think

juhoteperi20:06:24

https://facebook.github.io/react/docs/conditional-rendering.html#preventing-component-from-rendering This is pretty much the same, but the solution in this example is to always include the component in tree but render empty content for it

juhoteperi20:06:36

Can't find docs about how keys/diffing works for static children. It was more obvious before when React keys where present in DOM.

juhoteperi20:06:11

This works also:

(defn loading [loading?]
  (if loading?
    [:div "Loading..."]))

(defn report [loading?]
  [:div
   [loading loading?]
   [report-iframe loading?]])

pesterhazy20:06:53

moving the loading indicator after the iframe would also work