3

整数入力フィールドと「+1」と「-1」の 2 つのボタンを備えた試薬コンポーネントがあります。ユーザーが次のことができるようにしたいと思います。

  • 入力フィールドに直接整数値を入力します
  • 「+1」をクリックして、入力フィールドの値を 1 増やします
  • 「-1」をクリックして、入力フィールドの値を 1 減らします。

また、リフレームを使用する場合、できるようにしたいです

  • ボタンの1つをクリックして入力または調整した後、値をリフレームのアプリデータベースに保存します
  • re-frame の app db で変更された場合、入力フィールドの値を更新します (たとえば、API サーバーから値を取得した後)。

どうすればそれを行うことができますか?

4

1 に答える 1

6

概要:

  • コンポーネント内でローカルに定義された試薬原子を使用して、整数値を保持します
  • reset!:on-changeとの値:on-click

re-frame の app db と統合するには、以下を追加する必要があります。

  • 電話re-frame/dispatchして:on-change:on-click
  • re-frame/subscribe値がリフレームの app db に保存されている場所に移動し、それに応じてローカル試薬アトムを更新します

試薬コンポーネントから始めて、いくつかのコードを見てみましょう。

(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!を取得するたびにアトムを -ing し:on-change、舞台裏でフィールドを再レンダリングすることを試薬/反応に任せることです。

また、フィールドアトムの値をインクリメント/デクリメントする: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)))

試薬アトムの -ing(re-frame/dispatch :integer-input-field-updated value)に加えて、入力フィールドの値が変化するたびに呼び出す必要があります。reset!ヘルパー関数を追加しましょう:

(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

これまでの内容を要約してみましょう。

  • 入力フィールドと、入力フィールドの値を変更する 2 つのボタン
  • 値を格納し、UI 要素を結び付ける試薬アトム
  • 値を格納する re-frame の app db 内の場所

app dbからローカル アトムの値の流れをサポートする方法が必要です。(: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)-> 入力フィールドを再レンダリング AGAIN

それはクールではない。情報が一方向にのみ流れていることを確認する必要があります。つまり、 への変更がローカル アトムへの変更をトリガーする:integer-valueべきではありませが、ローカル アトムへの変更はに伝搬されます。基本的に、UI はそれ自体が開始する更新に反応するべきではありません。データベースに別のキーを導入することでこれを達成し、UI から来ていないすべての更新を強制的に両方を更新することができます。このキーを呼び出して、サブとハンドラーを作成しましょう。:integer-valuere-frame/subscribe: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)}]])))

そして今、すべてのコードを 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)}]])))
于 2016-01-29T18:46:26.363 に答える