0

Let's say I have a component defined like this:

(defn test-comp
  [timespan-ratom]
  (let [[timespan-lower timespan-upper] @timespan-ratom]
    (fn []
      [:div.row (str "Testing" timespan-lower timespan-upper)])))

timespan-ratom is defined globally in the component that instantiates test-comp like this:

(def timespan-ratom (ratom nil))

It's instantiated like this:

[test-comp timespan-ratom]

The first time the timespan-ratom is used to create test-comp "Testing" will be printed with the correct timespan-lower and timespan-upper value. But when the timespan-ratom is reset (reset!) the values are not updated in the test-comp component? Why is this?

It works when I change the function to this:

(defn test-comp
  [timespan-ratom]
    (fn []
      [:div.row (str "Testing" (first @timespan-ratom) (second @timespan-ratom))]))

Now the reason why I can't simply do like this is that in my actual production code I have local ratom that is dependent on the value of the timespan-ratom:

(defn test-comp
  [timespan-ratom]
  (let [[timespan-lower timespan-upper] @timespan-ratom
        selected-time (ratom timespan-upper)]
    (fn []
      ,,,)))

I.e. the initial value of selected-time should be the timespan-upper value defined by the timespan-ratom. The selected-time ratom is then changed locally from the test-comp component.

How can I solve this?

Johan
  • 37,479
  • 32
  • 149
  • 237

1 Answers1

1

Remove the inner function nesting from test-comp:

(defn test-comp
  [timespan-ratom]
  (let [[timespan-lower timespan-upper] @timespan-ratom]
    [:div.row (str "Testing" timespan-lower timespan-upper)]))

When you use an inner function with no arguments, the component can never receive the updated ratom, so it will forever hold on to the first value it got from the ratom on the first render. Also, you don't need the inner function here, because you don't have any local state.

If you do have local state (some state that needs to be remembered over the lifetime of the component), update your component so the inner function has the same arguments as the outer function and dereference the atom in the inner function:

(defn test-comp
  [ratom]
  (let [x "local-state"]
    (fn [ratom]
      (let [v @ratom]
        [:div x v]))))
Michiel Borkent
  • 34,228
  • 15
  • 86
  • 149
  • I didn't show the inner state in the simplified example but I do indeed have inner state which is affected by the `selected-time` ratom. – Johan Dec 23 '16 at 14:40
  • Then you need to add the same argument list of the outer function to the inner function. I edited my answer accordingly. – Michiel Borkent Dec 23 '16 at 14:59
  • 1
    If the inner state which is affected by the selected-time ratom is solely derivable from that ratom it isn't really component local state though. It's just one or more values computed on each render. Please refer to https://github.com/Day8/re-frame/wiki/Creating-Reagent-Components for details. – Michiel Borkent Dec 23 '16 at 15:07
  • 1
    Just want to emphasise the importance of the above link from @MichaelBorkent. There is lots of really good background material regarding reagent on the Day8 site. Understanding that material will same you LOTS of hours of learning the hard way! – Tim X Dec 24 '16 at 01:12