8

我有几个选择,但似乎都有点滞后,我认为应该有更好的选择。我只是希望能够创建表单,甚至动态地创建它们(例如,从我的应用程序中向表单添加行),并对不同输入的值进行适当的响应/重新框架/反应访问。

不确定这些中的任何一个是否是最好的选择,因为它们都在每个:on-change...

选项 #1 - 更新:on-change到全局原子

[:input {:value       @new-job-form
         :on-change   #(dispatch [:new-job-form (-> % .-target .-value)])}]

(reg-event-db
 :new-job-form
 (fn [db [_ v]]
   (assoc db :new-job-form v)))

选项#2 - 更新一些本地状态,只发送到全局原子:on-blur

(defn text-input
  "adapted from:
  https://yogthos.net/posts/2016-09-25-ReagentComponents.html

  The big idea is this holds local state, and pushes it to the global
  state only when necessary"
  [{:keys [sub-path disp]}]
  (r/with-let [value    (r/atom nil)
               focused? (r/atom false)]
    [:div
     [:input
      {:type      :text
       :on-focus  #(do (reset! value @(subscribe sub-path))
                       (reset! focused? true))
       :on-blur   #(do (dispatch (conj disp @value))
                       (reset! focused? false))
       :value     (if @focused? @value @(subscribe sub-path))
       :on-change #(reset! value (-> % .-target .-value))}]]))

第二个选项稍微不那么滞后,但比原始文本输入更滞后......

编辑:

选项 #3 - 为了完整起见,改编自 re-frame 的 TODOMVC 的风格略有不同

(defn text-input
  "adapted from re-frame's TODOMVC:
      https://github.com/Day8/re-frame/blob/master/examples/todomvc/src/todomvc/views.cljs

  note: this is one-way bound to the global atom, it doesn't subscribe to it"
  [{:keys [on-save on-stop props]}]
  (let [inner (r/atom "")]
    (fn [] [:input (merge props
                          {:type        "text"
                           :value       @inner
                           :on-blur     (on-save @inner)
                           :on-change   #(reset! inner (-> % .-target .-value))
                           :on-key-down #(case (.-which %)
                                           13 (on-save @inner) ; enter
                                           27 (on-stop) ; esc
                                           nil)})])))

[text-input {:on-save #(dispatch [:new-job-form {:path [:a]
                                                         :v    %}])
                     :on-stop #(js/console.log "stopp")
                     :props   {:placeholder "url"}}]
4

1 回答 1

2

Re-frame 和reagent+React在较低级别上,尝试将重新渲染限制为更改的组件。在您的情况下,如果除了文本字段之外的另一个组件(或整个 UI)重新呈现,则可能会导致延迟,这是唯一改变的事情。

基于您的“选项一”的示例:

(defn busy-wait [ms]
  (let [start (.getTime (js/Date.))]
    (while (< (.getTime (js/Date.)) (+ start ms)))))

(defn slow-component []
  (busy-wait 2000)
  (.log js/console "Ouch!")
  [:h2 "I was busy"])

(defn main-panel []
  (let [new-job-form (re-frame/subscribe [:new-job-form])
    (fn []
      [:div.container-fluid
        (slow-component)
        [:input       {:value    @new-job-form
         :on-change   #(dispatch [:new-job-form (-> % .-target .-value)])}]
;; etc

这会导致slow-component每次输入文本时都重新渲染,并且非常滞后,因为slow-component渲染至少需要 2000 毫秒。

在上述情况下,一个简单的解决方案是提供slow-component作为函数来重新框架,将调用更改为向量,即:

[:div.container-fluid
  [slow-component]

这允许 re-frame 看到slow-component不需要重新渲染,因为它的数据没有改变。当我们在原始示例中自己调用函数时,我们跳过了这个推理:

[:div.container-fluid
  (slow-component)

一个好的做法是在绑定到订阅时使用Form-2 组件。

于 2017-07-01T10:01:16.950 回答