我有一个带有整数输入字段和两个按钮“+1”和“-1”的试剂组件。我想让用户能够:
- 直接在输入栏中输入一个整数值
- 点击“+1”将输入字段中的值增加 1
- 单击“-1”将输入字段中的值减 1
另外,在使用re-frame时,我希望能够
- 在输入或通过单击其中一个按钮进行调整后,将值保存到重新构建的应用数据库
- 如果输入字段的值在 re-frame 的应用程序数据库中发生变化(例如,在我们从某个 API 服务器获取值之后),则更新该值
我该怎么做呢?
最佳答案
大纲:
- 使用组件内部本地定义的试剂原子来保存整数值
reset!
:on-change
和:on-click
中的值
为了与 re-frame 的应用程序数据库集成,我们需要添加以下内容:
- 在
:on-change
和:on-click
中调用re-frame/dispatch
re-frame/subscribe
值存储在 re-frame 的应用程序数据库中的位置,并相应地更新本地试剂原子
让我们看一些代码,从试剂组件开始:
(defn integer-field [default-value]
(let [int-atom (atom default-value)]
(fn []
[:div
[:input {:type "text"
:value @int-atom
:on-change #(reset! int-atom (-> % .-target .-value))}]
[:button {:on-click #(adjust-int int-atom 1)} "+"]
[:button {:on-click #(adjust-int int-atom -1)}]])))
诀窍在于 reset!
- 每当我们收到 :on-change
事件并让 reagent/react 负责重新渲染场在幕后。
另请注意,:on-click
调用另一个函数 adjust-int
来增加/减少字段原子的值。在这里:
(defn- adjust-int [int-atom delta]
(let [v (js/parseInt @int-atom)
valid? (not (js/Number.isNaN v))
new-v (+v delta)]
(when valid?
(reset! int-atom new-v))))
这按原样工作。但是,如果我们决定将整数值存储在 re-frame 中,我们确实需要更多代码。首先,让我们创建一个处理程序,用于存储从数据库中的输入字段接收到的值:
(re-frame/register-handler
:integer-input-field-updated
(fn [db [_ value]]
(assoc db :integer-value value)))
我们现在需要调用 (re-frame/dispatch :integer-input-field-updated value)
除了 reset 之外,每次输入字段的值发生变化!
-ing 试剂原子。让我们为它添加一个辅助函数:
(defn- store-int-value [int-atom value]
(reset! int-atom value)
(re-frame/dispatch :integer-input-field-updated value))
我们现在需要在每次值准备好存储时调用此函数而不是 reset!
。让我们首先在 adjust-int
中进行更改:
(defn- adjust-int [int-atom delta]
(let [v (js/parseInt @int-atom)
valid? (not (js/Number.isNaN v))
new-v (+v delta)]
(when valid?
(store-int-value int-atom new-v)))) ;; <---- changed
让我们回顾一下到目前为止的内容:
- 输入字段和两个改变输入字段值的按钮
- 存储值并将 UI 元素联系在一起的试剂原子
- re-frame 的应用程序数据库中存储值的位置
我们需要一种方法来支持值在另一个方向上的流动,从应用程序数据库到本地原子。我们可以连接一个订阅,以响应对 (:integer-value @db)
的更改并重置我们的本地原子。等等,这听起来像是某种循环,不是吗?让我们想象一下流程:
:点击
(reset!int-atom)
-> 重新渲染输入框(dispatch [:integer-input-field-updated new-value])
(assoc db :integer-value value)
- 试剂 react 被触发
(reset! int-atom)
-> 再次重新渲染输入字段
这不是很酷。我们需要确保信息只在一个方向上流动,这意味着对 :integer-value
的更改应该不会触发对本地原子的更改,但是更改为本地原子 do 传播到 :integer-value
。基本上,我们的 UI 不应该对它自己发起的更新使用react。我们可以通过在数据库中引入另一个键来实现这一点,re-frame/subscribe
到它,并强制所有未更新的 :integer-value
从用户界面更新两者。让我们称这个键为 :integer-value-input-field
并为它创建一个 sub 和一个处理程序:
(re-frame/register-sub
:integer-field-input-value
(fn [db _]
(reaction (:integer-field-input-value @db))))
(re-frame/register-handler
:integer-value-updated ;; <--- for use by other parts of the app
(fn [db [_ value]]
(assoc db :integer-field value :integer-field-input-value value)))
最后,让我们重构integer-field
组件:
(defn integer-field [default-value]
(let [int-atom (atom default-value)
the-int (re-frame/subscribe [:integer-field-input-value])] ;; <--- source of truth
(fn []
(reset! int-atom @the-int) ;; <--- spread the truth
[:div
[:input {:type "text"
:value @int-atom
:on-change #(store-int-value int-atom(-> % .-target .-value))}]
[:button {:on-click #(adjust-int int-atom 1)} "+"]
[:button {:on-click #(adjust-int int-atom -1)}]])))
现在,所有代码都集中在一个地方......
(re-frame/register-sub
:integer-field-input-value
(fn [db _]
(reaction (:integer-field-input-value @db))))
;; to be used by other parts of the app
(re-frame/register-handler
:integer-value-updated
(fn [db [_ value]]
(assoc db :integer-field value :integer-field-input-value value)))
;; to be used by the integer-field component
(re-frame/register-handler
:integer-input-field-updated
(fn [db [_ value]]
(assoc db :integer-value value)))
(defn- store-int-value [int-atom value]
(reset! int-atom value)
(re-frame/dispatch :integer-input-field-updated value))
(defn- adjust-int [int-atom delta]
(let [v (js/parseInt @int-atom)
valid? (not (js/Number.isNaN v))
new-v (+v delta)]
(when valid?
(store-int-value int-atom new-v))))
(defn integer-field [default-value]
(let [int-atom (atom default-value)
the-int (re-frame/subscribe [:integer-field-input-value])]
(fn []
(reset! int-atom @the-int)
[:div
[:input {:type "text"
:value @int-atom
:on-change #(store-int-value int-atom(-> % .-target .-value))}]
[:button {:on-click #(adjust-int int-atom 1)} "+"]
[:button {:on-click #(adjust-int int-atom -1)}]])))
关于clojurescript - 如何通过在试剂/重新框架中键入和从代码更新相同的输入字段?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35091827/