Fork me on GitHub
#reagent
<
2020-06-23
>
p-himik18:06:39

Suppose I have this component:

(defn panel [value]
  (r/with-let [f (fn []
                   (do-something-with value))]
    [:button {:on-click f}
     (create-str-label value)]))
If value changes, the button will be displayed with a new label. But f will still reference the old one, right? And to force it to reference the new value, I have to rerender the whole component, and the only way to do that would be to set :key from the outside and make it depend on value, right?

p-himik18:06:21

Just tested it - all assumptions seem to be correct, except for the "the only way" part. As an alternative, I can create a level of indirection via an intermediate ratom (or I guess it can or even should be a regular atom since I don't rely on it for rendering):

(defn panel [value]
  (r/with-let [inner-value (r/atom value)
               f (fn []
                   (do-something-with @inner-value))]
    (reset! inner-value value)
    [:button {:on-click f}
     (create-str-label value)]))
Looks ugly but at least it doesn't force me to come up with a way to derive a :key from a value which can be any value. Is it a known pattern? I don't think I've ever seen it documented anywhere.

wxitb201702:06:00

looks quite like the useCallback hook provded by native react

neo255104:06:52

Why do you not create a function that takes inner value as argument?

neo255104:06:30

And call f with the value on click?

neo255104:06:43

Like {:on-click #(f @inner-value)}

p-himik06:06:58

@ It goes to show once again that I should stop working earlier in the day, when I can still think. :) Thanks! Creating a function in that way is exactly the patter that I've been using forever in other places, but here I just blanked on it.

p-himik06:06:20

But a solution with an extra atom might not be that bad if you have many such functions. Hmm.

neo255106:06:31

Simple > ease

neo255106:06:25

I guess it depends if you want to test your views. Because you could also give the functions as argument?

p-himik06:06:55

That wouldn't make sense in my case. I don't want to parameterize behavior.

neo255106:06:23

Make a function that returns a map of function xD

p-himik06:06:36

I would say there's a fuzzy border separating the two approaches - the one with recreating functions and the one with wrapping the data in an extra atom. What to choose depends on the particular scenario. How to choose - hard to say, maybe it'll come with more experience of using the extra atom approach.

neo255106:06:06

Probably as long as you hide the implementation, it is fine.

neo255106:06:53

Actually I follow the same pattern of forms with subscriptions.

neo255106:06:33

The reason is I don’t want the text field input to go through all the reframe mechanism, but still have the desire to control the content.

neo255106:06:47

The additional atom acts as local state and cache.

p-himik06:06:54

Oh yeah, but the need in re-frame apps is a bit different. Also, apart from just avoiding extra data churning, it's a good pattern to control intermediate values when a user is still typing. And I think it's the only pattern to preserve the cursor position, but this one is implemented by Reagent itself, so you only need to think about it if you use some third-party components that Reagent doesn't know about.