Simplify props handling

Call Cloact components with separate cljsArgs and cljsChildren
props. That simplifies (and speeds up) code, for the small
price of one call to subvec.
This commit is contained in:
Dan Holmsand 2014-01-08 15:08:29 +01:00
parent 8a8779c736
commit c6b56a7a76
3 changed files with 69 additions and 79 deletions

View File

@ -23,39 +23,21 @@
(defn state [this] (defn state [this]
(.-cljsState this)) (.-cljsState this))
;; We store the "args" (i.e a vector like [comp props child1]) (defn js-props [C]
;; in .-cljsArgs, with optional props, which makes access a bit (aget C "props"))
;; tricky. The upside is that we don't have to do any allocations.
(defn args-of [C]
(-> C (aget "props") .-cljsArgs))
(defn props-in-args [args]
(let [p (nth args 1 nil)]
(when (map? p) p)))
(defn props-in-props [props] (defn props-in-props [props]
(-> props .-cljsArgs props-in-args)) (-> props .-cljsProps))
(defn- first-child [args] (defn cljs-props [C]
(let [p (nth args 1 nil)] (-> C js-props props-in-props))
(if (or (nil? p) (map? p)) 2 1)))
(defn- cljs-props [C]
(-> C args-of props-in-args))
(defn get-children [C] (defn get-children [C]
(let [args (args-of C) (-> C js-props .-cljsChildren))
c (first-child args)]
(drop c args)))
(defn replace-props [C newprops] (defn replace-props [C newprops]
(let [obj (js-obj)] (let [obj (js-obj)]
(set! (.-cljsArgs obj) (set! (.-cljsProps obj) newprops)
(apply vector
(nth (args-of C) 0)
newprops
(get-children C)))
(.setProps C obj))) (.setProps C obj)))
(defn set-props [C newprops] (defn set-props [C newprops]
@ -105,13 +87,15 @@
(fn [C nextprops nextstate] (fn [C nextprops nextstate]
;; Don't care about nextstate here, we use forceUpdate ;; Don't care about nextstate here, we use forceUpdate
;; when only when state has changed anyway. ;; when only when state has changed anyway.
(let [a1 (args-of C) (let [inprops (aget C "props")
a2 (-> nextprops .-cljsArgs)] p1 (.-cljsProps inprops)
(assert (vector? a1)) c1 (.-cljsChildren inprops)
p2 (.-cljsProps nextprops)
c2 (.-cljsChildren nextprops)]
(if (nil? f) (if (nil? f)
(not (util/equal-args a1 a2)) (not (util/equal-args p1 c1 p2 c2))
;; Call f with oldprops, newprops ;; call f with oldprops newprops oldchildren newchildren
(f (props-in-args a1) (props-in-args a2))))) (f p1 p2 c1 c2))))
:componentWillUnmount :componentWillUnmount
(fn [C] (fn [C]
@ -170,8 +154,14 @@
(let [spec (cljsify body) (let [spec (cljsify body)
res (.createClass React spec) res (.createClass React spec)
f (fn [& args] f (fn [& args]
(let [arg (js-obj)] (let [arg (js-obj)
(set! (.-cljsArgs arg) (apply vector res args)) props (nth args 0 nil)
hasmap (map? props)
first-child (if (or hasmap (nil? props)) 1 0)]
(set! (.-cljsProps arg) (if hasmap props {}))
(set! (.-cljsChildren arg)
(if (> (count args) first-child)
(subvec args first-child)))
(res arg)))] (res arg)))]
(set! (.-cljsReactClass f) res) (set! (.-cljsReactClass f) res)
(set! (.-cljsReactClass res) res) (set! (.-cljsReactClass res) res)

View File

@ -65,18 +65,20 @@
(defn wrapped-render [this comp id-class] (defn wrapped-render [this comp id-class]
(let [inprops (aget this "props") (let [inprops (aget this "props")
args (.-cljsArgs inprops) props (.-cljsProps inprops)
props (nth args 1 nil)
hasprops (or (nil? props) (map? props)) hasprops (or (nil? props) (map? props))
jsargs (->> (if hasprops (drop 1 args) args) jsargs (->> (.-cljsChildren inprops)
(map-into-array as-component))] (map-into-array as-component))]
(aset jsargs 0 (convert-props (if hasprops props) id-class)) (.unshift jsargs (convert-props props id-class))
(.apply comp nil jsargs))) (.apply comp nil jsargs)))
(defn wrapped-should-update [C nextprops nextstate] (defn wrapped-should-update [C nextprops nextstate]
(let [a1 (-> C (aget "props") .-cljsArgs) (let [inprops (aget C "props")
a2 (-> nextprops .-cljsArgs)] p1 (.-cljsProps inprops)
(not (util/equal-args a1 a2)))) c1 (.-cljsChildren inprops)
p2 (.-cljsProps nextprops)
c2 (.-cljsChildren nextprops)]
(not (util/equal-args p1 c1 p2 c2))))
(defn wrap-component [comp extras] (defn wrap-component [comp extras]
(.createClass React (js-obj "render" (.createClass React (js-obj "render"
@ -129,10 +131,15 @@
(defn vec-to-comp [v] (defn vec-to-comp [v]
(assert (pos? (count v))) (assert (pos? (count v)))
(let [[tag props] v (let [[tag props] v
hasmap (map? props)
first-child (if (or hasmap (nil? props)) 2 1)
c (as-class tag) c (as-class tag)
jsprops (js-obj)] jsprops (js-obj)]
(set! (.-cljsArgs jsprops) v) (set! (.-cljsProps jsprops) (if hasmap props {}))
(when (map? props) (set! (.-cljsChildren jsprops)
(if (> (count v) first-child)
(subvec v first-child)))
(when hasmap
(let [key (:key props)] (let [key (:key props)]
(when-not (nil? key) (when-not (nil? key)
(aset jsprops "key" key)))) (aset jsprops "key" key))))

View File

@ -1,4 +1,5 @@
(ns cloact.impl.util) (ns cloact.impl.util
(:require [cloact.debug :refer-macros [dbg]]))
(deftype partial-ifn [f args ^:mutable p] (deftype partial-ifn [f args ^:mutable p]
IFn IFn
@ -34,22 +35,24 @@
(assert (map? p1)) (assert (map? p1))
(merge-style p1 (merge-class p1 (merge p1 p2)))))) (merge-style p1 (merge-class p1 (merge p1 p2))))))
(defn identical-parts [v1 v2 from] (defn identical-parts [v1 v2]
;; Compare two vectors, from item with index "from", using identical? ;; Compare two vectors using identical?
(or (identical? v1 v2)
(let [end (count v1)] (let [end (count v1)]
(loop [n from] (and (== end (count v2))
(loop [n 0]
(if (>= n end) (if (>= n end)
true true
(if (identical? (nth v1 n) (nth v2 n)) (if (identical? (nth v1 n) (nth v2 n))
(recur (inc n)) (recur (inc n))
false))))) false)))))))
(def -not-found (js-obj)) (def -not-found (js-obj))
(defn shallow-equal-maps [x y] (defn shallow-equal-maps [x y]
;; Compare two maps, using keyword-identical? on all values ;; Compare two maps, using keyword-identical? on all values
(or (identical? x y) (or (identical? x y)
(when (== (count x) (count y)) (and (== (count x) (count y))
(reduce-kv (fn [res k v] (reduce-kv (fn [res k v]
(let [yv (get y k -not-found)] (let [yv (get y k -not-found)]
(if (or (keyword-identical? v yv) (if (or (keyword-identical? v yv)
@ -63,17 +66,7 @@
(reduced false)))) (reduced false))))
true x)))) true x))))
(defn equal-args [v1 v2] (defn equal-args [p1 c1 p2 c2]
;; Compare two "args" vectors, i.e things like [:div {:foo "bar} "baz"], [p1 c1 p2 c2]
;; using identical? on all individual parts. (and (identical-parts c1 c2)
;; The first bit (e.g the :div is assumed to be identical). (shallow-equal-maps p1 p2)))
(or (identical? v1 v2)
(let [c1 (count v1)]
(and (== c1 (count v2))
(if (< c1 2)
true
(let [props1 (nth v1 1)]
(if (or (nil? props1) (map? props1))
(and (identical-parts v1 v2 2)
(shallow-equal-maps props1 (nth v2 1)))
(identical-parts v1 v2 1))))))))