How to handle input elements in Clojure Re-Frame?

I have a couple of options, but both seem a bit laggy, and I think there should be a better alternative. I just would like to be able to create forms, even dynamically create them (for example, add lines to a form from my application) and have the appropriate reagent / re-frame / appropriate access to the values ​​of various inputs.

Not sure if either of these is a better alternative, as they both run functions after each :on-change ...

Option # 1 - update :on-change in the global atom

 [: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))) 

Option # 2 - update some local state, which sends only to the global atom :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))}]])) 

The second option is a bit less lagged, but more laggy than just the original text input ...

EDIT:

Option No. 3 - For completeness, a slightly different flavor adapted from the repeated frame of TODOMVC

 (defn text-input "adapted from re-frame 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"}}] 
+5
source share
1 answer

Re-frame and reagent + React at a lower level, try limiting re-rendering for changing components. In your case, lag may occur if another component (or the entire user interface) re-displays in addition to the text field, and this is the only thing that has changed.

An example of building your "Option one":

 (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 

This leads to re-rendering of the slow-component with each text input and is really lagging, since the slow-component takes at least 2000 ms to render.

In the above case, a simple solution is to provide a slow-component as a function for re-cropping, changing the call to a vector, that is:

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

This allows the re-frame to see that the slow-component does not need to be re-rendered since its data has not changed. We will skip this argument when we call the function directly in the original example:

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

It’s also good practice to use Form-2 components when binding to subscriptions.

0
source

Source: https://habr.com/ru/post/1258187/


All Articles