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

View File

@ -65,18 +65,20 @@
(defn wrapped-render [this comp id-class]
(let [inprops (aget this "props")
args (.-cljsArgs inprops)
props (nth args 1 nil)
props (.-cljsProps inprops)
hasprops (or (nil? props) (map? props))
jsargs (->> (if hasprops (drop 1 args) args)
jsargs (->> (.-cljsChildren inprops)
(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)))
(defn wrapped-should-update [C nextprops nextstate]
(let [a1 (-> C (aget "props") .-cljsArgs)
a2 (-> nextprops .-cljsArgs)]
(not (util/equal-args a1 a2))))
(let [inprops (aget C "props")
p1 (.-cljsProps inprops)
c1 (.-cljsChildren inprops)
p2 (.-cljsProps nextprops)
c2 (.-cljsChildren nextprops)]
(not (util/equal-args p1 c1 p2 c2))))
(defn wrap-component [comp extras]
(.createClass React (js-obj "render"
@ -129,10 +131,15 @@
(defn vec-to-comp [v]
(assert (pos? (count v)))
(let [[tag props] v
hasmap (map? props)
first-child (if (or hasmap (nil? props)) 2 1)
c (as-class tag)
jsprops (js-obj)]
(set! (.-cljsArgs jsprops) v)
(when (map? props)
(set! (.-cljsProps jsprops) (if hasmap props {}))
(set! (.-cljsChildren jsprops)
(if (> (count v) first-child)
(subvec v first-child)))
(when hasmap
(let [key (:key props)]
(when-not (nil? 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]
IFn
@ -34,46 +35,38 @@
(assert (map? p1))
(merge-style p1 (merge-class p1 (merge p1 p2))))))
(defn identical-parts [v1 v2 from]
;; Compare two vectors, from item with index "from", using identical?
(let [end (count v1)]
(loop [n from]
(if (>= n end)
true
(if (identical? (nth v1 n) (nth v2 n))
(recur (inc n))
false)))))
(defn identical-parts [v1 v2]
;; Compare two vectors using identical?
(or (identical? v1 v2)
(let [end (count v1)]
(and (== end (count v2))
(loop [n 0]
(if (>= n end)
true
(if (identical? (nth v1 n) (nth v2 n))
(recur (inc n))
false)))))))
(def -not-found (js-obj))
(defn shallow-equal-maps [x y]
;; Compare two maps, using keyword-identical? on all values
(or (identical? x y)
(when (== (count x) (count y))
(reduce-kv (fn [res k v]
(let [yv (get y k -not-found)]
(if (or (keyword-identical? v yv)
;; hack to allow cloact.core/partial and :style
;; maps to be compared with =
(and (or
(keyword-identical? k :style)
(identical? (type v) partial-ifn))
(= v yv)))
res
(reduced false))))
true x))))
(and (== (count x) (count y))
(reduce-kv (fn [res k v]
(let [yv (get y k -not-found)]
(if (or (keyword-identical? v yv)
;; hack to allow cloact.core/partial and :style
;; maps to be compared with =
(and (or
(keyword-identical? k :style)
(identical? (type v) partial-ifn))
(= v yv)))
res
(reduced false))))
true x))))
(defn equal-args [v1 v2]
;; Compare two "args" vectors, i.e things like [:div {:foo "bar} "baz"],
;; using identical? on all individual parts.
;; The first bit (e.g the :div is assumed to be identical).
(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))))))))
(defn equal-args [p1 c1 p2 c2]
[p1 c1 p2 c2]
(and (identical-parts c1 c2)
(shallow-equal-maps p1 p2)))