diff --git a/src/reagent/impl/batching.cljs b/src/reagent/impl/batching.cljs index 6d06f0b..38b7f44 100644 --- a/src/reagent/impl/batching.cljs +++ b/src/reagent/impl/batching.cljs @@ -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] diff --git a/src/reagent/impl/template.cljs b/src/reagent/impl/template.cljs index 6ab8339..31074f3 100644 --- a/src/reagent/impl/template.cljs +++ b/src/reagent/impl/template.cljs @@ -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))] - (.! this :cljsInputValue value) - (when-not (nil? value) - (batch/mark-rendered this) + (if (and (.' jsprops hasOwnProperty "onChange") + (.' jsprops hasOwnProperty "value")) + (let [v (.' jsprops :value) + value (if (nil? v) "" v) + on-change (.' jsprops :onChange)] + (.! this :cljsInputValue value) + (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)