Improve managed inputs

Make sure they are reset to their current value just after the
next render (if they are not changed during that rendering).
This commit is contained in:
Dan Holmsand 2014-11-13 19:42:48 +01:00
parent b1bec33448
commit 92fdf36aa5
2 changed files with 44 additions and 22 deletions

View File

@ -34,22 +34,31 @@
(when (.' c :cljsIsDirty)
(.' c forceUpdate)))))
(deftype RenderQueue [^:mutable queue ^:mutable scheduled?]
(defn run-funs [a]
(dotimes [i (alength a)]
((aget a i))))
(deftype RenderQueue [^:mutable queue ^:mutable scheduled?
^:mutable after-render]
Object
(queue-render [this c]
(.push queue c)
(.schedule this))
(add-after-render [_ f]
(.push after-render f))
(schedule [this]
(when-not scheduled?
(set! scheduled? true)
(next-tick #(.run-queue this))))
(run-queue [_]
(let [q queue]
(let [q queue aq after-render]
(set! queue (array))
(set! after-render (array))
(set! scheduled? false)
(run-queue q))))
(run-queue q)
(run-funs aq))))
(def render-queue (RenderQueue. (array) false))
(def render-queue (RenderQueue. (array) false (array)))
(defn flush []
(.run-queue render-queue))
@ -61,6 +70,13 @@
(defn mark-rendered [c]
(.! c :cljsIsDirty false))
(defn do-after-flush [f]
(.add-after-render render-queue f))
(defn do-later [f]
(do-after-flush f)
(.schedule render-queue))
;; Render helper
(defn is-reagent-component [c]

View File

@ -88,33 +88,39 @@
;;; Specialization for input components
(defn input-unmount [this]
(.! this :cljsInputValue nil))
(defn input-set-value [this]
(when-some [value (.' this :cljsInputValue)]
(.! this :cljsInputDirty false)
(let [node (.' this getDOMNode)]
(when (not= value (.' node :value))
(.! node :value value)))))
(defn input-handle-change [this on-change e]
(let [res (on-change e)]
;; Make sure the input is re-rendered, in case on-change
;; wants to keep the value unchanged
(batch/queue-render this)
(when-not (.' this :cljsInputDirty)
(.! this :cljsInputDirty true)
(batch/do-later #(input-set-value this)))
res))
(defn input-did-update [this]
(let [value (.' this :cljsInputValue)]
(when-not (nil? value)
(let [node (.' this getDOMNode)]
(when (not= value (.' node :value))
(.! node :value value))))))
(defn input-render-setup [this jsprops]
;; Don't rely on React for updating "controlled inputs", since it
;; doesn't play well with async rendering (misses keystrokes).
(let [on-change (.' jsprops :onChange)
value (when-not (nil? on-change)
(.' jsprops :value))]
(if (and (.' jsprops hasOwnProperty "onChange")
(.' jsprops hasOwnProperty "value"))
(let [v (.' jsprops :value)
value (if (nil? v) "" v)
on-change (.' jsprops :onChange)]
(.! this :cljsInputValue value)
(when-not (nil? value)
(batch/mark-rendered this)
(js-delete jsprops "value")
(doto jsprops
(.! :defaultValue value)
(.! :value nil)
(.! :onChange #(input-handle-change this on-change %))))))
(.! :onChange #(input-handle-change this on-change %))))
(.! this :cljsInputValue nil)))
(defn input-component? [x]
(let [DOM (.' js/React :DOM)]
@ -148,8 +154,8 @@
(defn add-input-methods [spec]
(doto spec
(.! :componentDidUpdate #(this-as c (input-did-update c)))
(.! :componentWillUnmount #(this-as c (batch/dispose c)))))
(.! :componentDidUpdate #(this-as c (input-set-value c)))
(.! :componentWillUnmount #(this-as c (input-unmount c)))))
(defn wrap-component [comp extras name]
(let [input? (input-component? comp)