组件中指定的 Ratom 无法更新该组件?

Ratom specified in component can't update that component?

刚开始使用 Reagent。我有一个按钮,其 :on-click 值导致 CPU 密集型功能变为 运行; return需要很长时间。我想更新按钮本身的文本以通知用户可能需要等待,所以我定义了一个 ratom 来指定按钮文本,然后我 reset! ratom 而函数是 运行宁。

如果我在我的按钮组件函数外定义了 ratom,这会起作用,但如果我通过 let 在组件函数内定义 ratom,或者如果我 reset! ratom 在组件功能的顶层。也就是说,如果我取消注释下面的第一行注释,或者取消注释 let 的两行,按钮文本将无法更改。难道我做错了什么?这是预期的行为吗?关于 ratoms 和 DOM-更新适用于此的一般规则是什么?

(def label (reagent.core/atom "Make chart"))

(defn chart-button
  [normal-label running-label]
;  (reset! label normal-label)                   ; reset globally-defined ratom
;  (let [label (reagent.core/atom normal-label)] ; use local ratom
    [:button {:on-click (fn []
                          (reset! label running-label)
                          (js/setTimeout (fn []
                                            (cpu-intensive-function)
                                            (reset! label normal-label))
                                         10))
              }
     @label] ;)
)

...
[chart-button "make chart" "running..."]
...

(不相关,但为了澄清:我使用 setTimeout 技巧 described here 来导致 DOM 更新,尽管 long-运行ning否则函数将阻止浏览器在函数 运行ning 时更新 DOM。)

您的问题是您的函数 chart-button 每次 组件需要(重新)渲染时都会被调用 。因此,例如在您的本地重置示例中,有人单击按钮,您的 label 将重置为 running-label。 Reagent 检测到此更改并调用您的 chart-button 函数以查看新呈现的按钮应该是什么样子,此时您的第一个重置更改又回来了。 let 版本也有类似的问题。

有几种方法可以处理 Reagent 中的局部状态。最简单的方法是 return 来自组件的函数而不是矢量,如本例所示。

(defn timer-component []
  (let [seconds-elapsed (r/atom 0)]
    (js/setInterval #(swap! seconds-elapsed inc) 1000)
    (fn []
      [:div
       "Seconds Elapsed: " @seconds-elapsed])))

基本上在 Reagent 中,您的组件可以是渲染函数,也可以是 returns 渲染函数的函数。在上面的例子中,我们使用闭包来设置一些本地状态,然后 return 一个使用该状态的渲染函数。每次 seconds-elapsed 递增时,都会再次调用内部函数并重新渲染组件。

另一种方法更复杂,但可能会帮助您更清楚地理解它。您可以使用 create-class 而不是将函数用作组件来完全控制组件生命周期。