0

Consider the following Reagent components:

(defn sub-compo [n]
  (let [state (r/atom {:colors (cycle [:red :green])})]
    (fn []
      [:div {:style {:color (-> @state :colors first)}
             :on-mouse-move #(swap! state update :colors rest)}
       "a very colorful representation of our number " n "."])))

(defn compo []
  (let [state (r/atom {:n 0})]
    (fn []
      [:div {:on-click #(swap! state update :n inc)}
       "Number is " (@state :n) "."
       [sub-compo (@state :n)]])))

I tried to make up an example, in which a sub component should depend on the state of its parent component. However the sub component should have an internal state as well. The above does not work properly. When the state in compo changes sub-compo is not initialized a new.

Which would be the way to go here, in order to let sub-compo be in sync with comp? Whenever the state of comp changes sub-comp should actually be initialized anew, meaning it's color state is set to the initial value again.


Here's a solution that does at least what I want. It uses a cursor and a watch. But maybe there is a simpler way to do so, anyways:

(defn sub-compo [n]
  (let [init-state {:colors (cycle [:red :green])}
        state (r/atom init-state)]
    (add-watch n :my (fn []
                       (reset! state init-state)))
    (fn []
      [:div {:style {:color (-> @state :colors first)}
             :on-mouse-move #(swap! state update :colors rest)}
       "a very colorful representation of our number " @n "."])))

(defn compo []
  (let [state (r/atom {:n 0})]
    (fn []
      [:div {:on-click #(swap! state update :n inc)}
       "Number is " (@state :n) "."
       [sub-compo (r/cursor state [:n])]])))
Anton Harald
  • 5,772
  • 4
  • 27
  • 61

1 Answers1

0

The above does not work properly. When the state in compo changes sub-compo is not initialized a new.

This is because the inner function of sub-compo needs to receive the argument n as well.

Whenever the state of comp changes sub-comp should actually be initialized anew, meaning it's color state is set to the initial value again.

You could use :component-will-receive-props for this.

This worked for me:

(defn sub-compo [n]
  (let [init {:colors (cycle [:red :green])}
        state (r/atom init)]
    (r/create-class
     {:component-will-receive-props
      (fn [this [_ n]]
        (reset! state init))
      :reagent-render
      (fn [n]
        [:div {:style {:color (-> @state :colors first)}
               :on-mouse-move #(swap! state update :colors rest)}
         "a very colorful representation of our number " n "."])})))
Michiel Borkent
  • 34,228
  • 15
  • 86
  • 149
  • great. This is exactly the snippet I puzzled in the meanwhile! I was wondering, that it has to be that verbose. I thought it's a quite useful pattern and there might be some reagent built ins already. But it looks like not. – Anton Harald Feb 18 '17 at 09:43
  • I'll leave it open for a while, maybe someone comes with a trick. Then I'll accept your answer. – Anton Harald Feb 18 '17 at 09:47
  • I second the approach above, which I discovered with pain as well. I then looked for alternatives and was suggested https://github.com/pesterhazy/recalcitrant – Andrea Richiardi Feb 19 '17 at 17:46