Fork me on GitHub
#reagent
<
2019-06-10
>
pcj19:06:35

So I've been struggling with this for a while and I can't seem to get my head around why my _TimeDisplay component keeps remounting on prop change. It doesn't when I follow https://github.com/reagent-project/reagent/blob/master/examples/material-ui/src/example/core.cljs but as soon as I put the code to add :classes into its own function it mounts.

(def with-custom-styles (withStyles #js{:padding #js{:padding 20}}))

(defn _TimeDisplay [_]
  (r/create-class
   {:component-did-mount #(print "Mounting _TimeDisplay")
    :reagent-render
    (fn [{:keys [classes format on-change value]}]
      [Grid {:classes {:root (:padding classes)} :container? true}
       [TextField {;:auto-focus? true
                   :default-value (.get (Moment value format) "hour")
                   :full-width? true
                   :label "Hour"
                   :margin "normal"
                   :on-change (set-time format on-change "hour" value)}]
       [TextField {:default-value (.get (Moment value format) "minute")
                   :full-width? true
                   :label "Minute"
                   :margin "normal"
                   :on-change (set-time format on-change "minute" value)}]
       [TextField {:default-value (.get (Moment value format) "second")
                   :full-width? true
                   :label "Second"
                   :margin "normal"
                   :on-change (set-time format on-change "second" value)}]])}))

(defn TimeDisplay [props]
  (r/create-class
   {:component-did-mount #(print "Mounting TimeDisplay")
    :reagent-render
    (fn [props]
      [:> (with-custom-styles (r/reactify-component
                               (fn [{:keys [classes]}]
                                 [_TimeDisplay
                                  (update props :classes merge (js->clj classes :keywordize-keys true))])))])}))

lilactown19:06:17

@pcj my best guess is because reagent caches the reactify-component call, but you are re-creating the function each render with (fn [{:keys [classes]}] ...

lilactown19:06:01

so each time it renders, reactify-component creates a new React class, and then React compares that class to the instance that was previously rendered and detects it’s a new component and unmounts the old one, mounts the new one

pcj20:06:48

Thanks for the help! So I changed the defn to def but it still mounts every single time so I think that i'm recreating that function with each render. I am calling TimeDisplay like :

:time [TimeDisplay {:format (:format props)
                                    :on-change time-change
                                    :value @*current-value*}]
within a condp. I have a macro that automatically adds the withStyles hoc that looks something like this:
:time4 (with-styles {:padding {:padding 20}}
                         [_TimeDisplay {:format (:format props)
                                        :on-change time-change
                                        :value @*current-value*}])
which works. I don't mind doing it that way but I don't really like the programmer having to inject the classes every single time the component is used.

lilactown19:06:30

also, I don’t htink you should be calling r/create-class inside of a function either. how are you calling TimeDisplay?

lilactown19:06:48

I think what you actually want is:

(def _TimeDisplay
  (r/create-class
   {:component-did-mount #(print "Mounting _TimeDisplay")
    :reagent-render
    (fn [{:keys [classes format on-change value]}]
      [Grid {:classes {:root (:padding classes)} :container? true}
       [TextField {;:auto-focus? true
                   :default-value (.get (Moment value format) "hour")
                   :full-width? true
                   :label "Hour"
                   :margin "normal"
                   :on-change (set-time format on-change "hour" value)}]
       [TextField {:default-value (.get (Moment value format) "minute")
                   :full-width? true
                   :label "Minute"
                   :margin "normal"
                   :on-change (set-time format on-change "minute" value)}]
       [TextField {:default-value (.get (Moment value format) "second")
                   :full-width? true
                   :label "Second"
                   :margin "normal"
                   :on-change (set-time format on-change "second" value)}]])}))

(def TimeDisplay
  (r/create-class
   {:component-did-mount #(print "Mounting TimeDisplay")
    :reagent-render
    (fn [props]
      [:> (with-custom-styles (r/reactify-component
                               (fn [{:keys [classes]}]
                                 [_TimeDisplay
                                  (update props :classes merge (js->clj classes :keywordize-keys true))])))])}))

lilactown19:06:57

@pcj ☝️ try that