我正在编写一个Clojurescript应用程序,使用Reagent使我的组件具有响应性。
我有一个简单的问题:我应该
- 通过我的组件将我的atoms作为输入传递,还是
- 将atoms作为全局变量使用,并让它们“副作用”我的组件?
在教程中,他们使用了后者,然而为了保持函数的纯净性,我选择了前者。
我是否正确地说,将它们用作全局变量(除了在定义组件输入时更少冗长)可以防止重新渲染不使用原子状态的整个父组件?
我正在编写一个Clojurescript应用程序,使用Reagent使我的组件具有响应性。
我有一个简单的问题:我应该
在教程中,他们使用了后者,然而为了保持函数的纯净性,我选择了前者。
我是否正确地说,将它们用作全局变量(除了在定义组件输入时更少冗长)可以防止重新渲染不使用原子状态的整个父组件?
如果您的组件接受原子作为参数,那么您可以使它们更加可重用和易于测试。
如果您选择将整个应用程序状态保留在单个原子中,然后使用游标将其传递给子组件,则尤其如此。
;; setup a single instance of global state
(defonce app-state
(reagent/atom {:foo 0 :bar 0})
;; define a generic counter component that knows
;; nothing about the global state
(defn counter
[count]
[:div
[:button {:onclick #(swap! count inc) "+"]
[:span @count]])
;; define counter components and give them access to
;; specific context within the global state
(defn app
[state]
[counter (reagent/cursor app-state [:foo])]
[counter (reagent/cursor app-state [:bar])])
如果你决定使用Reagent和Re-frame,你甚至可以更进一步。Re-frame鼓励您使用特定的架构构建您的应用程序,看起来像这样。
app-db > subscriptions
^
handlers v
^
events < components
与其直接编写组件并将其连接到全局原子(app-db
),你需要编写 subscriptions
,它们只是从app-db
中选择/查询数据并在app-db
发生更改时将其传递给组件的函数。
然后,组件不再直接操作app-db
,而是创建events
,这些事件只是描述组件目的的小型数据。
这些事件会发送到handlers
,这些处理程序是函数,它们将event
和当前的app-db
作为参数,并返回一个新的app-db
。然后现有的app-db
被替换,触发订阅者向下传递数据到组件等等。
如果您发现自己的Reagent项目有点混乱,阅读Re-frame readme对您肯定会有所帮助,无论您是否决定使用它。
我更喜欢将ratom传递给组件。 Re-frame越来越流行了:https://github.com/Day8/re-frame
传递ratom并不会使您的函数更加纯净,因为原子仍可能受到副作用的影响。但是,它确实使您的组件更具灵活性和可重用性,因为它们定义其依赖项。
无论是引用全局db还是传入的db,都不会直接影响重新渲染过程。重新渲染的信号图是从向量中deref的出现构建的,它不关心ratom来自何处。但是,您可以通过创建反应来提高效率。
(defn my-component []
(let [x (reaction (:x @db)]
(fn []
[:div @x]))
只有当 :x 变化时,此组件才会重新渲染(而不是当数据库中的任何内容变化时)。创建反应可能会变得繁琐,这是 re-frame 吸引人的地方之一。
(ns whip.view.reactions
(:require [reagent.core :as reagent]
[devcards.core :refer-macros [defcard-rg deftest]])
(:require-macros [reagent.ratom :refer [reaction]]))
(def a (reagent/atom {:x 100 :y 200})) (def b (reaction (:x @a)))
(def c (reaction (+ @b 10)))
(defn view-c []
(prn "Rendering view-c") [:div
[:div @c]
[:button {:on-click (fn [e] (swap! a update :x inc))} "inc x"]
[:button {:on-click (fn [e] (swap! a update :y inc))} "inc y"]])
(defcard-rg reaction-example [view-c])
反应是一种非常简洁的表达数据流的方法。在这里,您从一个包含x和y值的ratom开始。然后构建一个仅观察x值的反应b。接下来,引入另一个观察b和10之和的反应c。然后创建一个以反应式方式呈现c的组件。请注意,当您单击“增加x”按钮时,视图会更新为应用表达式结果。当您单击“增加y”按钮时,不会发生任何事情。检查控制台以确认只有在单击“增加x”时才会打印“Rendering view-c”消息,这是很好的事情,因为视图不以任何方式依赖于y。如果您在视图中解除a而不是c,则即使y发生变化,它也将被重新渲染。Reagent通过requestAnimationFrame对反应和ratoms做出反应。因此,更改许多ratoms和依赖于它们的反应仅会导致一个渲染阶段。
reaction
的写作,其中包括了示例。 - Timothy Pratleyreaction
的具体示例,希望有所帮助... 如果不清楚,请告诉我。 - Timothy Pratley