Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/reagent/dom/client.cljs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
(ns reagent.dom.client
(:require ["react" :as react]
["react-dom/client" :as react-dom-client]
["react-dom" :as react-dom]
[reagent.impl.batching :as batch]
[reagent.impl.protocols :as p]
[reagent.impl.template :as tmpl]
Expand Down Expand Up @@ -52,6 +53,7 @@
([^js root el]
(render root el tmpl/*current-default-compiler*))
([^js root el compiler]
(set! batch/react-flush react-dom/flushSync)
(let [comp (fn [] (p/as-element compiler el))
js-props #js {}]
(set! (.-comp js-props) comp)
Expand All @@ -62,6 +64,7 @@
(hydrate-root container el nil))
([container el {:keys [compiler on-recoverable-error identifier-prefix]
:or {compiler tmpl/*current-default-compiler*}}]
(set! batch/react-flush react-dom/flushSync)
(let [js-props #js {}
comp (fn [] (p/as-element compiler el))]
(set! (.-comp js-props) comp)
Expand Down
44 changes: 12 additions & 32 deletions src/reagent/impl/batching.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,16 @@
fake-raf)
w))))

(defn compare-mount-order
[^clj c1 ^clj c2]
(- (.-cljsMountOrder c1)
(.-cljsMountOrder c2)))

(defn run-queue [a]
;; sort components by mount order, to make sure parents
;; are rendered before children
(.sort a compare-mount-order)
(dotimes [i (alength a)]
(let [^js/React.Component c (aget a i)]
(when (true? (.-cljsIsDirty c))
(.forceUpdate c)))))

;; See reagent.dom.client/render and hydrate-root
;;
;; On React 19 react-dom/flushSync is used to flush all Reagent ratom changes
;; to DOM right away, to avoid double batching for these.
;; React strongly recommends to avoid flushSync, but run-queue is
;; used just once per animation frame and only when there were component
;; re-renders triggered from ratom changes, so maybe this is fine.
(defonce react-flush
(fn noop [f]
(f)))

;; Set from ratom.cljs
(defonce ratom-flush (fn []))
Expand All @@ -58,11 +54,6 @@
(set! scheduled? true)
(next-tick #(.run-queues this))))

(queue-render [this c]
(when (nil? (.-componentQueue this))
(set! (.-componentQueue this) #js []))
(enqueue this (.-componentQueue this) c))

(add-before-flush [this f]
(when (nil? (.-beforeFlush this))
(set! (.-beforeFlush this) #js []))
Expand All @@ -82,20 +73,14 @@
(set! (.-beforeFlush this) nil)
(run-funs fs)))

(flush-render [this]
(when-some [fs (.-componentQueue this)]
(set! (.-componentQueue this) nil)
(run-queue fs)))

(flush-after-render [this]
(when-some [fs (.-afterRender this)]
(set! (.-afterRender this) nil)
(run-funs fs)))

(flush-queues [this]
(.flush-before-flush this)
(ratom-flush)
(.flush-render this)
(react-flush (fn [] (ratom-flush)))
(.flush-after-render this)))

(def render-queue (->RenderQueue false))
Expand All @@ -107,12 +92,7 @@
(.flush-after-render render-queue))

(defn queue-render [^clj c]
(when-not (.-cljsIsDirty c)
(set! (.-cljsIsDirty c) true)
(.queue-render render-queue c)))

(defn mark-rendered [^clj c]
(set! (.-cljsIsDirty c) false))
(.forceUpdate c))

(defn do-before-flush [f]
(.add-before-flush render-queue f))
Expand Down
6 changes: 0 additions & 6 deletions src/reagent/impl/component.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,6 @@
(fn componentWillUnmount []
(this-as c
(some-> (gobj/get c "cljsRatom") ratom/dispose!)
(batch/mark-rendered c)
(when-not (nil? f)
(.call f c c))))

Expand Down Expand Up @@ -255,7 +254,6 @@
(this-as c (if util/*non-reactive*
(do-render c compiler)
(let [^clj rat (gobj/get c "cljsRatom")]
(batch/mark-rendered c)
(if (nil? rat)
(ratom/run-in-reaction #(do-render c compiler) c "cljsRatom"
batch/queue-render rat-opts)
Expand Down Expand Up @@ -306,7 +304,6 @@
(construct this props))
(when get-initial-state
(set! (.-state this) (get-initial-state this)))
(set! (.-cljsMountOrder this) (batch/next-mount-count))
this))]

(gobj/extend (.-prototype cmp) (.-prototype react/Component) methods)
Expand Down Expand Up @@ -412,7 +409,6 @@
_ (when-not (.-current state-ref)
(let [obj #js {}]
(set! (.-forceUpdate obj) force-update)
(set! (.-cljsMountOrder obj) (batch/next-mount-count))
;; Use reagentRender name, as that is also used
;; by class components, and some checks.
;; reagentRender is replaced with form-2 inner fn,
Expand All @@ -438,8 +434,6 @@
;; so reaction fn will always see the latest value.
(set! (.-argv reagent-state) argv)

(batch/mark-rendered reagent-state)

;; static-fns :render
(if (nil? rat)
(ratom/run-in-reaction
Expand Down
19 changes: 17 additions & 2 deletions test/reagenttest/testreagent.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,13 @@
val (r/atom 0)
secval (r/atom 0)
reaction-ran (atom 0)
v1 (reaction (swap! reaction-ran inc) @val)
v1 (reaction
(js/console.log "v1 reaction run")
(swap! reaction-ran inc)
@val)
comp (fn []
(swap! ran inc)
(js/console.log "comp render")
[:div (str "val " @v1 " " @val " " @secval)])]
(u/async
(u/with-render [div [comp]]
Expand All @@ -93,12 +97,23 @@
(is (= "val 0 0 0" (.-innerText div)))
(is (= 1 @ran))

;; secval goes back to the original value, it shouldn't trigger render.
;; val changes twice, ending up in a new value -> one render.
;; val also triggers v1 reaction -> but the change should happen
;; during the same RAF -> still just one render.
(u/act
(js/console.log "1")
(reset! secval 1)
(js/console.log "2")
(reset! secval 0)
(js/console.log "3")
(reset! val 1)
(js/console.log "4")
(reset! val 2)
(reset! val 1))
(js/console.log "5")
(reset! val 1)
(js/console.log "6")
)

(is (= "val 1 1 0" (.-innerText div)))
(is (= 2 @ran) "ran once more")
Expand Down
Loading