clojurescript 原子的哪些更改会导致试剂组件重新呈现?

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

考虑以下试剂成分。它使用一个 ref 函数,该函数根据 span 元素的实际大小更新本地状态原子。这样做是为了重新渲染显示其自身大小的组件

(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)]])))

如果执行 get-real-size returns 向量,日志消息会不断打印,这意味着组件一直不必要地重新渲染。如果它 returns 只是一个数字或字符串,则日志只出现两次 - 如本场景中所预期的那样。

这是什么原因?是否可能在内部用新向量(尽管包含相同的值)更新 clojure 脚本原子意味着将另一个 JavaScript 对象放在那里,从而改变原子?而放置一个值不会产生可观察到的变化?只是猜测...*

无论如何 - 对于实际用例,将跨度的大小保存在向量中肯定会更好。有什么方法可以实现吗?

我在尝试增强 问题中给出的答案时偶然发现了这一点。


* 因为在 JS 中:({} === {}) // false

您可以通过 not= 检查解决此问题:

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

我不确定向量与其他值不同的原因是什么。

我想我已经找到了为什么 vector 的行为与 string/number 不同的答案。当旧值和新值之间的 identical? returns 为假时,试剂将试剂原子计数为 "changed"(并因此更新依赖它的组件)。请参阅 this tutorial 中的副标题 "changed?":

For ratoms, identical? is used (on the value inside the ratom) to determine if a new value has changed with regard to an old value.

然而,事实证明 identical? 对向量和 strings/ints 的行为不同。如果你启动 clj 或 cljs repl,你会看到:

(identical? 1 1)
;; true 
(identical? "a" "a")
;; true 
(identical? [1] [1])
;; false
(identical? ["a"] ["a"])
;; false

如果您查看 identical? 的作用 here,您会发现它测试其参数是否 相同 object .我认为底层内部数据表示是这样的,在 clojure 中,"a" 始终与自身相同 object,而包含相同值的两个向量彼此不同 object .

确认:对于普通原子而不是试剂原子,我们可以看到字符串标识在原子重置时得以保留,而矢量标识则不然。

(def a1 (atom "a"))
(let [aa @a1] (reset! a1 "a") (identical? aa @a1))
;; true
(def a2 (atom ["a"]))
(let [aa @a2] (reset! a2 ["a"]) (identical? aa @a2))
;; false

它的重新渲染就像它应该基于它的编写方式一样。您正在使用与重置原子相同的函数取消引用原子。我总是把它们分开。

(defn span-size [size]
  [:span (prn-str @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-size]])))