概要:
- コンポーネント内でローカルに定義された試薬原子を使用して、整数値を保持します
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)
変更に反応してローカル アトムをリセットするサブスクリプションを接続できます。ちょっと待って、それはある種のループのようですね。流れを視覚化しましょう:
:on-click
(reset! int-atom)
-> 入力フィールドを再レンダリング
(dispatch [:integer-input-field-updated new-value])
(assoc db :integer-value value)
- 試薬反応が引き起こされる
(reset! int-atom)
-> 入力フィールドを再レンダリング AGAIN
それはクールではない。情報が一方向にのみ流れていることを確認する必要があります。つまり、 への変更がローカル アトムへの変更をトリガーする:integer-value
べきではありませんが、ローカル アトムへの変更はに伝搬されます。基本的に、UI はそれ自体が開始する更新に反応するべきではありません。データベースに別のキーを導入することでこれを達成し、UI から来ていないすべての更新を強制的に両方を更新することができます。このキーを呼び出して、サブとハンドラーを作成しましょう。:integer-value
re-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)}]])))