I have a couple options, but both seem a bit laggy, and I'm thinking that there should be a better alternative. I just would like to be able to create forms, even dynamically create them (eg adding rows to a form from within my app), and have reagent/re-frame/react-appropriate access to the values of the different inputs.
Not sure if either of these are the best alternative though, since they both run functions after every :on-change
...
Option #1 - update :on-change
into 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, that only dispatches 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 slightly less laggy, but more laggy than just a raw text input...
EDIT:
Option #3 - for completeness, a slightly different flavor adapted from re-frame's 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"}}]
re-frame is a ClojureScript framework for building user interfaces. It has a data-oriented, functional design.
re-frame is a functional framework for writing SPAs in ClojureScript, using Reagent. Being a functional framework, it is about two things: data. the functions which transform that data.
Re-frame gives you a way to capture the intent of UI actions (Events) and turn them into changes in the application state and the world outside of the browser (Effects). Further, it gives you a way to generate HTML based on changes to the application state (Components).
Re-frame, and reagent
+ React
on a lower level, try to limit re-rendering to components that change. In your case, lagginess may result if another component (or the whole UI) re-renders in addition to the text field, which was the only thing that changed.
An example building on 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 slow-component
re-rendering every time text is entered, and is really laggy, since slow-component
takes at least 2000 ms to render.
In the above case, a simple solution is to provide the slow-component
as a function to re-frame, changing the call into a vector, i.e.:
[:div.container-fluid
[slow-component]
This allows re-frame to see that the slow-component
does not need re-rendering, as its data has not changed. We skip this reasoning, when we call the function ourselves in the original example:
[:div.container-fluid
(slow-component)
A good practise is also to use Form-2 components when binding to subscriptions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With