【问题标题】:How to update same input field by typing and from code in reagent/re-frame?如何通过键入和从试剂/重新框架中的代码更新相同的输入字段?
【发布时间】:2016-01-30 02:46:26
【问题描述】:

我有一个带有整数输入字段和两个按钮“+1”和“-1”的试剂组件。我想让用户能够:

  • 直接在输入字段中输入整数值
  • 单击“+1”可将输入字段中的值增加 1
  • 点击“-1”将输入字段中的值减1

另外,在使用re-frame时,我希望能够

  • 在输入或通过单击其中一个按钮进行调整后,将值保存到 re-frame 的应用程序数据库
  • 如果输入字段的值在 re-frame 的应用数据库中发生更改(例如,在我们从某个 API 服务器获取值之后),则更新输入字段的值

我该怎么做呢?

【问题讨论】:

    标签: clojurescript reagent re-frame


    【解决方案1】:

    总纲:

    • 使用组件内本地定义的试剂原子来保存整数值
    • reset! :on-change:on-click 中的值

    为了与re-frame的app db集成,我们需要添加以下内容:

    • :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)}]])))
    

    诀窍在于,每次我们收到:on-change 事件时,reset!-ing 原子并让 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))))
    

    按原样工作。但是,如果我们决定将整数值存储在重新帧中,我们确实需要更多代码。首先,让我们创建一个处理程序来存储它从 db 中的输入字段接收到的值:

    (re-frame/register-handler
      :integer-input-field-updated
      (fn [db [_ value]]
        (assoc db :integer-value value)))
    

    现在,除了reset!-ing 试剂原子之外,每次输入字段的值发生变化时,我们都需要调用(re-frame/dispatch :integer-input-field-updated value)。让我们为其添加一个辅助函数:

    (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) 的更改做出反应并重置我们的本地原子。等等,这听起来像是某种循环,不是吗?让我们可视化流程:

    1. :on-click
    2. (reset! int-atom) -> 重新渲染输入字段
    3. (dispatch [:integer-input-field-updated new-value])
    4. (assoc db :integer-value value)
    5. 试剂反应被触发
    6. (reset! int-atom) -> 再次重新渲染输入字段

    这不酷。我们需要确保信息只在一个方向流动,这意味着对:integer-value 的更改应该触发对本地原子的更改,但对本地原子的更改 em> 传播到:integer-value。基本上,我们的 UI 不应该对它自己发起的更新做出反应。我们可以通过在数据库中引入另一个键 re-frame/subscribe 来实现这一点,并强制所有不是来自 UI 的更新到 :integer-value 来更新两者。让我们将此键称为 :integer-value-input-field 并为其创建一个子程序和一个处理程序:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-10-25
      • 1970-01-01
      • 1970-01-01
      • 2020-09-19
      • 2015-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多