Fork me on GitHub
#reagent
<
2019-10-27
>
joshkh11:10:09

is there a clever way to recursively call parent component functions that haven't yet been defined? for example, component-a expects component-b to be defined, while component-b expects component-a to be defined:

(defn component-b [children]
  (if (coll? children)
    ; component-a not yet defined, compile error
    [:li [component-a children]]
    [:li (str "leaf: " children)]))

(defn component-a [tree]
  (into [:ul]
        (map (fn [[k v]] [component-b v]) tree)))

p-himik11:10:12

(declare component-a)?

joshkh13:10:51

thank you!

Pavel Klavík19:10:45

Hi, I am trying to use react-spring within reagent and I am not yet very familiar with using React components. How should I rewrite the first example into Reagent: https://www.react-spring.io/docs/props/spring ??

Pavel Klavík19:10:30

In the end, I got the following wrapper working, but not sure whether this is done in the most useful way:

(defn spring
  [attr component input-fn]
  [:> rspring/Spring attr
   (fn [props] (-> #(-> % (js->clj :keywordize-keys true)
                        component)
                   r/reactify-component
                   (r/create-element (clj->js (input-fn props)))))])
Used as
[spring {:from {:opacity 0}
                     :to   {:opacity 1}}
        (fn [props]
          [:div props "Hello world!"])
        (fn [props] {:style props})]

Pavel Klavík23:10:10

I am probably doing something wrong since it runs very slowly on larger components. In each animation frame, the entire dom subtree is completely replaced.

p-himik05:10:00

Do you have a repo where that could be reproduced?

p-himik13:10:48

1. You're doing too much JS<->CLJS churning. Just don't do that at all in your code. If you really have to change something, prefer JS interop or cljs-bean. 2. Don't call components directly as functions and don't create unnecessary lambdas. 3. react-spring itself says here https://www.react-spring.io/docs/props/spring: "Of course the only properties that the browser can animate relatively cheaply are composite properties (opacity, transform), so you might want to watch out for that."

p-himik13:10:03

Here's a simplified version of your code that also works a bit faster:

(defn spring [attr component]
  [:> rspring/Spring attr
   (fn [props]
     (r/create-element
       (r/reactify-component component)
       props))])

(defn unit [{:keys [left]}]
  [:div.unit {:on-click #(rf/dispatch [:update-position])
              :style    {:top  100
                         :left left}}
   [:div.title "Title of unit"]
   [:div.content
    [:div "Click this unit to move it."]
    [:img {:src    "/img/Clojure_logo.png"
           :width  300
           :height 300}]]])

(defn main-panel []
  (let [position @(rf/subscribe [:position])]
    [spring {:to {:left (if (= position :left)
                          100
                          500)}}
     unit]))

p-himik13:10:11

Not sure whether it's relevant to what you're doing and to Spring in general, but you may be interested in the FLIP approach: https://aerotwist.com/blog/flip-your-animations/

Pavel Klavík14:10:26

Thanks for the simplification, it now runs much better, in particular, it correctly updates only the style instead of replacing the entire subtree on every animation frame.

Pavel Klavík14:10:27

One problem left in my main code is that at the beginning of the animation, the entire component subtree is completely replaced which makes the animation slower. The reason is that I have a component data in a map obtained by a subscription, and whenever these data are changed, we have a new component function. (The second paramater for spring is (partial component subscription-data).)

Pavel Klavík14:10:27

Would it help to move the subscription inside the component?

p-himik14:10:42

Yeah, probably. Trying it certainly won't hurt. 😉

Pavel Klavík15:10:50

It seemed to help. So basically it is not good to use partial in components because any change in function parameters will cause rerendering of the entire subtree?

p-himik15:10:55

AFAIK providing functions will make React rerender the component since the function will always be created right where it's defined, and even if you don't change anything, the new function will not be identical to the old one.

Pavel Klavík15:10:08

thanks for the links, will take a look

Pavel Klavík15:10:51

currently, we are not using the power of subscriptions enought and a lot of logic is moved into views which is not ideal, so I plan to improve subscriptions quite a lot in future

p-himik15:10:00

Yeah, you definitely want to move as much data churning and logic into subscriptions as possible, but still within reason. You also may want to use subscription signals (subs that feed values to other subs) and maybe even raw subs. There's a lot of small things that are definitely worth knowing.