组件中指定的 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 而不是将函数用作组件来完全控制组件生命周期。
刚开始使用 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 而不是将函数用作组件来完全控制组件生命周期。