I'm trying to build a Hello World app with Reagent/React. I tie an input with an atom using :value
/:on-change
combo. However, when I type, the input remains empty:
(defn new-user []
; Atom declared in an inner `let`.
(let [v (atom "")]
[:div
[:input {:type "text"
:value @v
:on-change (fn [evt]
(reset! v (-> evt .-target .-value)))
}]]))
(defn app [state]
[:div
[:p "State" (pr-str @app-state)]
[new-user]])
(r/render-component [app app-state]
(.-body js/document))
If I move the atom out of let
, it will work:
; Atom is now top-level.
(defonce v (atom ""))
(defn new-user []
[:div
[:input {:type "text"
:value @v
:on-change (fn [evt]
(reset! v (-> evt .-target .-value)))
}]])
Is it because the v
will be redeclared each time React re-renders? If so, how do I refactor that code so I don't have to use global variable to hold v
?
I turned out to be pretty easy - the "component" function can return not a template, but a function returning template. This way the outer function will be executed only once (and we can put let
there), and inner will be executed on every re-render:
(defn new-user []
(let [v (atom "")]
; The returned function will be called on every re-render.
(fn []
[:div
[:input {:type "text"
:value @v
:on-change (fn [evt]
(reset! v (-> evt .-target .-value)))}]])))
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