Fork me on GitHub
#reagent
<
2021-07-13
>
ChillPillzKillzBillz15:07:17

if the state has a vector of substates... like

state (ratom {:statex <val>
                       :statevector [
                                              {:substatey <val>
                                               :substatez <val>
                                              }]
                      })
how to construct a track for 1 element of the statevector?

p-himik15:07:52

You might want a cursor instead.

p-himik15:07:17

With a track - same thing, but you will have to use get-in yourself.

p-himik15:07:59

Note that vectors are indexed and associative collections. So many of the functions that work on maps will also work on vectors:

user=> (def v [:a :b :c])
#'user/v
user=> (get v 1)
:b
user=> (def m {:a v})
#'user/m
user=> (get-in m [:a 1])
:b

ChillPillzKillzBillz15:07:22

I don't want the entire chain of components to be updated when one element in the :statevector gets updated... I know I can create a track for certain keys .e.g. (track #(select-keys @state [:text :width :height])) but in this case the keys :text :width etc are at the same level... but how will we deref to the particular element of the vector?

p-himik15:07:39

I'm not sure what you mean. Can you provide an example of some input data, and the corresponding example of the output data that some f that you want to write will produce?

ChillPillzKillzBillz15:07:49

e.g.

state (ratom {:text "asdf"
              :width 45
              :height 30
              :childstates [{:childid 1
                             :childtext "asdf"}
                            {:childid 1
                             :childtext "asdf"}]})
If I am to create a track for just the text, width, height I can create it by (track #(select-keys @state [:text :width :height])) But now I want to create a track/cursor doesn't really matter for every childstate element... because they are rendered as separate reagent components. I want only the component tied to the childstate element which has been modified to be updated.... is (track (get #(select-keys @state [:childstates]) 1)) enough/right?

p-himik15:07:57

You will still iterate over :childstates to create the children in the first place, no? Otherwise, how do you know that that 1 above is indeed 1 and not all values from 0 to 42? While you're learning, don't overcomplicate things unless there's a measurable performance issue.

p-himik15:07:32

And when you notice that you're creating more and more tracks/cursors/reactions on top of one large global state, just try to re-frame. Or maybe even earlier - as soon as you're somewhat comfortable with Reagent.

ChillPillzKillzBillz15:07:22

yes there is a (for [childstate (:childstates @state)]... there... yes

ChillPillzKillzBillz15:07:10

yes 1 is just an example... I need to extract the state from inside a loop as shown above

p-himik15:07:02

That's the thing - you don't need to do that. Just use childstate as is, no need for tracks - children that receive the same childstate shouldn't be re-rendered.

p-himik15:07:00

With the initial state, the first child will be something like [child-component {:childid 1, :childtext "asdf"}]. And after you change e.g. :width, it will still be the same exact vector - so it won't be re-rendered.

ChillPillzKillzBillz15:07:47

ok maybe again I have a gap in my understanding... just to confirm... we need track to reduce the number of keys that we need to track the changes to for a given component... right? So am I right in assuming if there is only 1 key in state that determines the component redraw, we don't need track?

p-himik15:07:12

And when use tracks like you wrote above, use reagent.ratom/reaction instead:

(reaction (select-keys @state [...]))
Quite a bit simpler.

p-himik15:07:37

If you pass a component some some state s, so you have a Hiccup vector like [component s], and then you use [component s] again - component should not be called again, the resulting Reagent element should stay the same and should not be re-rendered. At least, that's my understanding. But when you write [component (assoc s :some-unused-key 1)], component will be called again. That's it. How you make sure that s doesn't receive unnecessary updates - up to you. Could be tracks, could be plain select-keys, could be nothing if you don't really care - chances are, component is a simple function that just generates a vector, and in this case it's not likely to impart performance in any way.

ChillPillzKillzBillz16:07:52

yes it is a simple function... so to take your example... if I have 42 children components, and only the 40th child state got changed, I don't want all 42 to be updated... as long as passing just the childstate while calling [childcomponent childstate] is enough to ensure that, I don't really want for more...

p-himik16:07:35

Since you use @state in the parent component, the parent component function will be called when any change to state is made. It will iterate over all items in :childstates, create all those vectors like [child-component s] again, and return the overall Hiccup vector (assuming you don't call child-component as a function. Then that Hiccup vector is recursively turned into a React element, and then it's returned back to React. At this point, child-component still has not been called. React will then determine what it needs to do to reconcile the previous and the current version of the tree. When it determines that properties of some component has changed, only then will a particular child-component or some other view function will be called. So in your example, the whole parent component will be re-rendered, and only one of its children will be re-rendered.

ChillPillzKillzBillz17:07:51

muchos gracias!

👍 2