Allow output from create-class to be used directly by React

This commit is contained in:
Dan Holmsand 2015-10-06 12:49:47 +02:00
parent 4601b37ec5
commit 533c05aaca
4 changed files with 57 additions and 60 deletions

View File

@ -3,7 +3,7 @@
[reagent.impl.batching :as batch] [reagent.impl.batching :as batch]
[reagent.ratom :as ratom] [reagent.ratom :as ratom]
[reagent.interop :refer-macros [.' .!]] [reagent.interop :refer-macros [.' .!]]
[reagent.debug :refer-macros [dbg prn dev? warn]])) [reagent.debug :refer-macros [dbg prn dev? warn warn-unless]]))
(declare ^:dynamic *current-component*) (declare ^:dynamic *current-component*)
@ -12,6 +12,16 @@
;;; Argv access ;;; Argv access
(defn shallow-obj-to-map [o]
(let [ks (js-keys o)
len (alength ks)]
(persistent!
(loop [m (transient {}) i 0]
(if (< i len)
(let [k (aget ks i)]
(recur (assoc! m (keyword k) (aget o k)) (inc i)))
m)))))
(defn extract-props [v] (defn extract-props [v]
(let [p (nth v 1 nil)] (let [p (nth v 1 nil)]
(if (map? p) p))) (if (map? p) p)))
@ -22,11 +32,13 @@
(if (> (count v) first-child) (if (> (count v) first-child)
(subvec v first-child)))) (subvec v first-child))))
(defn props-argv [p] (defn props-argv [c p]
(.' p :argv)) (if-some [a (.' p :argv)]
a
[c (shallow-obj-to-map p)]))
(defn get-argv [c] (defn get-argv [c]
(.' c :props.argv)) (props-argv c (.' c :props)))
(defn get-props [c] (defn get-props [c]
(-> (get-argv c) extract-props)) (-> (get-argv c) extract-props))
@ -37,6 +49,12 @@
(defn reagent-component? [c] (defn reagent-component? [c]
(-> (get-argv c) nil? not)) (-> (get-argv c) nil? not))
(defn cached-react-class [c]
(.' c :cljsReactClass))
(defn cache-react-class [c constructor]
(.! c :cljsReactClass constructor))
;;; State ;;; State
@ -137,22 +155,22 @@
;; 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 [old-argv (.' c :props.argv) (let [old-argv (.' c :props.argv)
new-argv (.' nextprops :argv)] new-argv (.' nextprops :argv)
(if (nil? f) no-argv (or (nil? old-argv) (nil? new-argv))]
(or (nil? old-argv) (cond
(nil? new-argv) (nil? f) (or no-argv (not= old-argv new-argv))
(not= old-argv new-argv)) no-argv (f c (get-argv c) (props-argv c nextprops))
(f c old-argv new-argv)))))) :else (f c old-argv new-argv))))))
:componentWillUpdate :componentWillUpdate
(fn [nextprops] (fn [nextprops]
(this-as c (this-as c
(f c (props-argv nextprops)))) (f c (props-argv c nextprops))))
:componentDidUpdate :componentDidUpdate
(fn [oldprops] (fn [oldprops]
(this-as c (this-as c
(f c (props-argv oldprops)))) (f c (props-argv c oldprops))))
:componentWillMount :componentWillMount
(fn [] (fn []
@ -268,8 +286,7 @@
{:pre [(map? body)]} {:pre [(map? body)]}
(let [spec (cljsify body) (let [spec (cljsify body)
res (.' js/React createClass spec)] res (.' js/React createClass spec)]
(util/cache-react-class res res) (cache-react-class res res)))
res))
(defn component-path [c] (defn component-path [c]
(let [elem (some-> (or (some-> c (.' :_reactInternalInstance)) (let [elem (some-> (or (some-> c (.' :_reactInternalInstance))
@ -295,23 +312,24 @@
"")) ""))
"")) ""))
(defn shallow-obj-to-map [o]
(into {} (for [k (js-keys o)]
[(keyword k) (aget o k)])))
(def elem-counter 0) (defn fn-to-class [f]
(assert (ifn? f) (str "Expected a function, not " (pr-str f)))
(warn-unless (not (and (fn? f)
(some? (.' f :type))))
"Using native React classes directly in Hiccup forms "
"is not supported. Use create-element or "
"adapt-react-class instead: " (.' f :type)
(comp-name))
(let [spec (meta f)
withrender (assoc spec :reagent-render f)
res (create-class withrender)]
(cache-react-class f res)))
(defn as-class [tag]
(if-some [cached-class (cached-react-class tag)]
cached-class
(fn-to-class tag)))
(defn reactify-component [comp] (defn reactify-component [comp]
(.' js/React createClass (as-class comp))
#js{:displayName "react-wrapper"
:render
(fn []
(this-as this
(as-element
[comp
(-> (.' this :props)
shallow-obj-to-map
;; ensure re-render, might get mutable js data
(assoc :-elem-count
(set! elem-counter
(inc elem-counter))))])))}))

View File

@ -196,26 +196,6 @@
:id id :id id
:className class})) :className class}))
(defn fn-to-class [f]
(assert (ifn? f) (str "Expected a function, not " (pr-str f)))
(warn-unless (not (and (fn? f)
(some? (.' f :type))))
"Using native React classes directly in Hiccup forms "
"is not supported. Use create-element or "
"adapt-react-class instead: " (.' f :type)
(comp/comp-name))
(let [spec (meta f)
withrender (assoc spec :reagent-render f)
res (comp/create-class withrender)
wrapf (util/cached-react-class res)]
(util/cache-react-class f wrapf)
wrapf))
(defn as-class [tag]
(if-some [cached-class (util/cached-react-class tag)]
cached-class
(fn-to-class tag)))
(defn get-key [x] (defn get-key [x]
(when (map? x) (when (map? x)
;; try catch to avoid clojurescript peculiarity with ;; try catch to avoid clojurescript peculiarity with
@ -229,7 +209,7 @@
(-> v (nth 1 nil) get-key))) (-> v (nth 1 nil) get-key)))
(defn reag-element [tag v] (defn reag-element [tag v]
(let [c (as-class tag) (let [c (comp/as-class tag)
jsprops #js{:argv v}] jsprops #js{:argv v}]
(some->> v key-from-vec (.! jsprops :key)) (some->> v key-from-vec (.! jsprops :key))
(.' js/React createElement c jsprops))) (.' js/React createElement c jsprops)))

View File

@ -8,12 +8,6 @@
;;; Props accessors ;;; Props accessors
(defn cached-react-class [c]
(.' c :cljsReactClass))
(defn cache-react-class [c constructor]
(.! c :cljsReactClass constructor))
;; Misc utilities ;; Misc utilities
(defn memoize-1 [f] (defn memoize-1 [f]

View File

@ -487,11 +487,14 @@
(deftest test-reactize-component (deftest test-reactize-component
(let [ae r/as-element (let [ae r/as-element
ce r/create-element ce r/create-element
c1r (fn [p] a (atom nil)
c1r (fn [p & args]
(reset! a args)
[:p "p:" (:a p) (:children p)]) [:p "p:" (:a p) (:children p)])
c1 (r/reactify-component c1r)] c1 (r/reactify-component c1r)]
(is (= (rstr [:p "p:a"]) (is (= (rstr [:p "p:a"])
(rstr (ce c1 #js{:a "a"})))) (rstr (ce c1 #js{:a "a"}))))
(is (= @a nil))
(is (= (rstr [:p "p:"]) (is (= (rstr [:p "p:"])
(rstr (ce c1 #js{:a nil})))) (rstr (ce c1 #js{:a nil}))))
(is (= (rstr [:p "p:"]) (is (= (rstr [:p "p:"])
@ -500,10 +503,12 @@
(is (= (rstr [:p "p:a" [:b "b"]]) (is (= (rstr [:p "p:a" [:b "b"]])
(rstr (ce c1 #js{:a "a"} (rstr (ce c1 #js{:a "a"}
(ae [:b "b"]))))) (ae [:b "b"])))))
(is (= @a nil))
(is (= (rstr [:p "p:a" [:b "b"] [:i "i"]]) (is (= (rstr [:p "p:a" [:b "b"] [:i "i"]])
(rstr (ce c1 #js{:a "a"} (rstr (ce c1 #js{:a "a"}
(ae [:b "b"]) (ae [:b "b"])
(ae [:i "i"]))))))) (ae [:i "i"])))))
(is (= @a nil))))
(deftest test-keys (deftest test-keys
(let [a nil ;; (r/atom "a") (let [a nil ;; (r/atom "a")