From 533c05aaca782eb1dd545e98d0a1e712d0b17eff Mon Sep 17 00:00:00 2001 From: Dan Holmsand Date: Tue, 6 Oct 2015 12:49:47 +0200 Subject: [PATCH] Allow output from create-class to be used directly by React --- src/reagent/impl/component.cljs | 80 +++++++++++++++++++------------ src/reagent/impl/template.cljs | 22 +-------- src/reagent/impl/util.cljs | 6 --- test/reagenttest/testreagent.cljs | 9 +++- 4 files changed, 57 insertions(+), 60 deletions(-) diff --git a/src/reagent/impl/component.cljs b/src/reagent/impl/component.cljs index ea7a782..5f0c83e 100644 --- a/src/reagent/impl/component.cljs +++ b/src/reagent/impl/component.cljs @@ -3,7 +3,7 @@ [reagent.impl.batching :as batch] [reagent.ratom :as ratom] [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*) @@ -12,6 +12,16 @@ ;;; 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] (let [p (nth v 1 nil)] (if (map? p) p))) @@ -22,11 +32,13 @@ (if (> (count v) first-child) (subvec v first-child)))) -(defn props-argv [p] - (.' p :argv)) +(defn props-argv [c p] + (if-some [a (.' p :argv)] + a + [c (shallow-obj-to-map p)])) (defn get-argv [c] - (.' c :props.argv)) + (props-argv c (.' c :props))) (defn get-props [c] (-> (get-argv c) extract-props)) @@ -37,6 +49,12 @@ (defn reagent-component? [c] (-> (get-argv c) nil? not)) +(defn cached-react-class [c] + (.' c :cljsReactClass)) + +(defn cache-react-class [c constructor] + (.! c :cljsReactClass constructor)) + ;;; State @@ -137,22 +155,22 @@ ;; Don't care about nextstate here, we use forceUpdate ;; when only when state has changed anyway. (let [old-argv (.' c :props.argv) - new-argv (.' nextprops :argv)] - (if (nil? f) - (or (nil? old-argv) - (nil? new-argv) - (not= old-argv new-argv)) - (f c old-argv new-argv)))))) + new-argv (.' nextprops :argv) + no-argv (or (nil? old-argv) (nil? new-argv))] + (cond + (nil? f) (or no-argv (not= old-argv new-argv)) + no-argv (f c (get-argv c) (props-argv c nextprops)) + :else (f c old-argv new-argv)))))) :componentWillUpdate (fn [nextprops] (this-as c - (f c (props-argv nextprops)))) + (f c (props-argv c nextprops)))) :componentDidUpdate (fn [oldprops] (this-as c - (f c (props-argv oldprops)))) + (f c (props-argv c oldprops)))) :componentWillMount (fn [] @@ -268,8 +286,7 @@ {:pre [(map? body)]} (let [spec (cljsify body) res (.' js/React createClass spec)] - (util/cache-react-class res res) - res)) + (cache-react-class res res))) (defn component-path [c] (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] - (.' js/React createClass - #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))))])))})) + (as-class comp)) diff --git a/src/reagent/impl/template.cljs b/src/reagent/impl/template.cljs index ef8daa7..7c70183 100644 --- a/src/reagent/impl/template.cljs +++ b/src/reagent/impl/template.cljs @@ -196,26 +196,6 @@ :id id :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] (when (map? x) ;; try catch to avoid clojurescript peculiarity with @@ -229,7 +209,7 @@ (-> v (nth 1 nil) get-key))) (defn reag-element [tag v] - (let [c (as-class tag) + (let [c (comp/as-class tag) jsprops #js{:argv v}] (some->> v key-from-vec (.! jsprops :key)) (.' js/React createElement c jsprops))) diff --git a/src/reagent/impl/util.cljs b/src/reagent/impl/util.cljs index ae174d6..af53fe4 100644 --- a/src/reagent/impl/util.cljs +++ b/src/reagent/impl/util.cljs @@ -8,12 +8,6 @@ ;;; Props accessors -(defn cached-react-class [c] - (.' c :cljsReactClass)) - -(defn cache-react-class [c constructor] - (.! c :cljsReactClass constructor)) - ;; Misc utilities (defn memoize-1 [f] diff --git a/test/reagenttest/testreagent.cljs b/test/reagenttest/testreagent.cljs index 12c526e..737d1e9 100644 --- a/test/reagenttest/testreagent.cljs +++ b/test/reagenttest/testreagent.cljs @@ -487,11 +487,14 @@ (deftest test-reactize-component (let [ae r/as-element ce r/create-element - c1r (fn [p] + a (atom nil) + c1r (fn [p & args] + (reset! a args) [:p "p:" (:a p) (:children p)]) c1 (r/reactify-component c1r)] (is (= (rstr [:p "p:a"]) (rstr (ce c1 #js{:a "a"})))) + (is (= @a nil)) (is (= (rstr [:p "p:"]) (rstr (ce c1 #js{:a nil})))) (is (= (rstr [:p "p:"]) @@ -500,10 +503,12 @@ (is (= (rstr [:p "p:a" [:b "b"]]) (rstr (ce c1 #js{:a "a"} (ae [:b "b"]))))) + (is (= @a nil)) (is (= (rstr [:p "p:a" [:b "b"] [:i "i"]]) (rstr (ce c1 #js{:a "a"} (ae [:b "b"]) - (ae [:i "i"]))))))) + (ae [:i "i"]))))) + (is (= @a nil)))) (deftest test-keys (let [a nil ;; (r/atom "a")