Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reagent input not updating

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?

like image 559
kamituel Avatar asked Feb 21 '15 11:02

kamituel


1 Answers

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)))}]])))
like image 114
kamituel Avatar answered Oct 13 '22 20:10

kamituel