Batch updates properly by rendering parents before children

This commit is contained in:
Dan Holmsand 2014-01-27 16:17:37 +01:00
parent eb381f13bb
commit dfe0eb41d5
4 changed files with 68 additions and 20 deletions

View File

@ -44,6 +44,7 @@ looking like this:
{:get-initial-state (fn [this])
:component-will-receive-props (fn [this new-props])
:should-component-update (fn [this old-props new-props old-children new-children])
:component-did-mount (fn [this])
:component-did-update (fn [this old-props old-children])
:component-will-unmount (fn [this])
:render (fn [props children this])}

View File

@ -2,7 +2,7 @@
(ns reagent.impl.component
(:refer-clojure :exclude [flush])
(:require [reagent.impl.template :as tmpl
:refer [cljs-props cljs-children React]]
:refer [cljs-props cljs-children cljs-level React]]
[reagent.impl.util :as util]
[reagent.ratom :as ratom]
[reagent.debug :refer-macros [dbg prn]]))
@ -51,17 +51,23 @@
(js/requestAnimationFrame f)
(js/setTimeout f 16)))
(defn run-queue [v]
(doseq [C v]
(when-not (.-cljsIsDirty C)
(dbg C))
(defn compare-levels [c1 c2]
(- (-> c1 js-props (aget cljs-level))
(-> c2 js-props (aget cljs-level))))
(defn run-queue [a]
;; sort components by level, to make sure parents
;; are rendered before children
(.sort a compare-levels)
(dotimes [i (alength a)]
(let [C (aget a i)]
(when (.-cljsIsDirty C)
(.forceUpdate C))))
(.forceUpdate C)))))
(deftype RenderQueue [^:mutable queue ^:mutable scheduled?]
Object
(queue-render [this C]
(set! queue (conj queue C))
(.push queue C)
(.schedule this))
(schedule [this]
(when-not scheduled?
@ -69,11 +75,11 @@
(next-tick #(.run-queue this))))
(run-queue [_]
(let [q queue]
(set! queue (empty queue))
(set! queue (array))
(set! scheduled? false)
(run-queue q))))
(def render-queue (RenderQueue. [] false))
(def render-queue (RenderQueue. (array) false))
(defn flush []
(.run-queue render-queue))
@ -90,7 +96,7 @@
;; Call render function with props, children, component
res (f props children C)
conv (if (vector? res)
(tmpl/as-component res)
(tmpl/as-component res (aget p cljs-level))
(if (fn? res)
(do-render C (set! (.-cljsRenderFn C) res))
res))]

View File

@ -9,6 +9,7 @@
(def cljs-props "cljsProps")
(def cljs-children "cljsChildren")
(def cljs-level "cljsLevel")
(def isClient (not (nil? (try (.-document js/window)
(catch js/Object e nil)))))
@ -64,10 +65,10 @@
(set-id-class objprops id-class))
objprops))))
(defn map-into-array [f coll]
(defn map-into-array [f arg coll]
(let [a (into-array coll)]
(dotimes [i (alength a)]
(aset a i (f (aget a i))))
(aset a i (f (aget a i) arg)))
a))
(declare as-component)
@ -75,9 +76,10 @@
(defn wrapped-render [this comp id-class]
(let [inprops (aget this "props")
props (aget inprops cljs-props)
level (aget inprops cljs-level)
hasprops (or (nil? props) (map? props))
jsargs (->> (aget inprops cljs-children)
(map-into-array as-component))]
(map-into-array as-component (inc level)))]
(.unshift jsargs (convert-props props id-class))
(.apply comp nil jsargs)))
@ -139,7 +141,7 @@
(set! (.-cljsReactClass tag) (wrap-component tag nil nil))
(fn-to-class tag)))))))
(defn vec-to-comp [v]
(defn vec-to-comp [v level]
(assert (pos? (count v)))
(let [[tag props] v
hasmap (map? props)
@ -147,14 +149,17 @@
c (as-class tag)
jsprops (js-obj cljs-props (if hasmap props)
cljs-children (if (> (count v) first-child)
(subvec v first-child)))]
(subvec v first-child))
cljs-level level)]
(when hasmap
(let [key (:key props)]
(when-not (nil? key)
(aset jsprops "key" key))))
(c jsprops)))
(defn as-component [x]
(cond (vector? x) (vec-to-comp x)
(seq? x) (map-into-array as-component x)
true x))
(defn as-component
([x] (as-component x 0))
([x level]
(cond (vector? x) (vec-to-comp x level)
(seq? x) (map-into-array as-component level x)
true x)))

View File

@ -12,6 +12,8 @@
(def isClient (not (nil? (try (.-document js/window)
(catch js/Object e nil)))))
(def rflush reagent/flush)
(defn add-test-div [name]
(let [doc js/document
body (.-body js/document)
@ -124,6 +126,40 @@
(is (= runs (running)))
(is (= 2 @ran)))))
(deftest batched-update-test []
(when isClient
(let [ran (atom 0)
v1 (atom 0)
v2 (atom 0)
c2 (fn [{val :val}]
(swap! ran inc)
(assert (= @v1 val))
[:div @v2])
c1 (fn []
(swap! ran inc)
[:div @v1
[c2 {:val @v1}]])]
(with-mounted-component [c1]
(fn [c div]
(rflush)
(is (= @ran 2))
(swap! v2 inc)
(is (= @ran 2))
(rflush)
(is (= @ran 3))
(swap! v1 inc)
(rflush)
(is (= @ran 5))
(swap! v2 inc)
(swap! v1 inc)
(rflush)
(is (= @ran 7))
(swap! v1 inc)
(swap! v1 inc)
(swap! v2 inc)
(rflush)
(is (= @ran 9)))))))
(deftest init-state-test
(when isClient
(let [ran (atom 0)