Search code examples
clojureclojurescriptreagent

Which changes to clojurescript atoms cause reagent components to re-render?


Consider the following reagent component. It uses a ref function, which updates a local state atom, based on the real size of a span element. This is done in order to re-render the component displaying its own size

(defn show-my-size-comp []
  (let [size (r/atom nil)]
    (fn []
      (.log js/console "log!")
      [:div
       [:span {:ref (fn [el]
                      (when el (reset! size (get-real-size el))))} 
        "Hello, my size is:" ]
       [:span (prn-str @size)]])))

If the implementation of get-real-size returns a vector, the log message is printed constantly, meaning the component unnecessarily being re-rendered all the time. If it returns just a number or a string, the log appears only twice - as intended in this scenario.

What's the reason for this? Is it maybe that updating an clojure script atom with a new vector (containing the same values though) internally means putting another JavaScript object there, thus changing the atom? Whereas putting a value produces no observable change? Just speculation...*

Anyways - for the real use case, saving the size of the span in a vector would certainly better.. Are there ways to achieve this?

I cam across this, when trying to enhance the answer given in this question.


* since in JS: ({} === {}) // false


Solution

  • You can work around the problem with a not= check:

                    (fn [el]
                      (when el
                        (let [s (get-real-size el)]
                          (when (not= s @size)
                            (reset! size s)))))
    

    I'm not sure what the reason is for why vectors should differ from other values.