Make sure lifecycle callbacks work when called as native component

This commit is contained in:
Dan Holmsand 2015-10-12 17:18:37 +02:00
parent fbbc5e72c6
commit 776c47491c
2 changed files with 145 additions and 22 deletions

View File

@ -39,13 +39,29 @@
(props-argv c (.' c :props)))
(defn get-props [c]
(-> (get-argv c) extract-props))
(let [p (.' c :props)]
(if-some [v (.' p :argv)]
(extract-props v)
(shallow-obj-to-map p))))
(defn get-children [c]
(-> (get-argv c) extract-children))
(let [p (.' c :props)]
(if-some [v (.' p :argv)]
(extract-children v)
(->> (.' p :children)
(.' util/react Children.toArray)
(into [])))))
(defn reagent-component? [c]
(-> (get-argv c) nil? not))
(defn ^boolean reagent-class? [c]
(and (fn? c)
(some? (some-> c .-prototype (.' :reagentRender)))))
(defn ^boolean react-class? [c]
(and (fn? c)
(some? (some-> c .-prototype (.' :render)))))
(defn ^boolean reagent-component? [c]
(some? (.' c :reagentRender)))
(defn cached-react-class [c]
(.' c :cljsReactClass))
@ -69,9 +85,6 @@
;;; Rendering
(defn ^boolean reagent-class? [c]
(and (fn? c)
(some? (some-> c .-prototype (.' :reagentRender)))))
(defn wrap-render [c]
(let [f (.' c :reagentRender)
@ -146,8 +159,8 @@
(this-as c (reset! (state-atom c) (.call f c c))))
:componentWillReceiveProps
(fn componentWillReceiveProps [props]
(this-as c (.call f c c (get-argv c))))
(fn componentWillReceiveProps [nextprops]
(this-as c (.call f c c (props-argv c nextprops))))
:shouldComponentUpdate
(fn shouldComponentUpdate [nextprops nextstate]
@ -257,9 +270,9 @@
(defn create-class [body]
{:pre [(map? body)]}
(let [spec (cljsify body)
res (.' util/react createClass spec)]
(cache-react-class res res)))
(->> body
cljsify
(.' util/react createClass)))
(defn component-path [c]
(let [elem (some-> (or (some-> c (.' :_reactInternalInstance))
@ -285,20 +298,21 @@
""))
""))
(defn fn-to-class [f]
(assert (ifn? f) (str "Expected a function, not " (pr-str f)))
(warn-unless (not (and (fn? f)
(some? (some-> f .-prototype (.' :render)))))
(warn-unless (not (and (react-class? f)
(not (reagent-class? f))))
"Using native React classes directly in Hiccup forms "
"is not supported. Use create-element or "
"adapt-react-class instead: " (let [n (util/fun-name f)]
(if (empty? n) f n))
(comp-name))
(let [spec (meta f)
withrender (assoc spec :reagent-render f)
res (create-class withrender)]
(cache-react-class f res)))
(if (reagent-class? f)
(cache-react-class f f)
(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)]
@ -306,4 +320,6 @@
(fn-to-class tag)))
(defn reactify-component [comp]
(as-class comp))
(if (react-class? comp)
comp
(as-class comp)))

View File

@ -728,6 +728,8 @@
comp (atom c1)
c2 (fn []
(apply vector @comp @arg))
cnative (fn []
(into [:> @comp] @arg))
check (fn []
(is (= (:initial-state @res)
{:at 1 :args [@t]}))
@ -741,7 +743,7 @@
(reset! arg ["a" "c"])
(r/flush)
(is (= (:will-receive @res)
{:at 5 :args [@t [@comp "a" "b"]]}))
{:at 5 :args [@t [@comp "a" "c"]]}))
(is (= (:should-update @res)
{:at 6 :args [@t [@comp "a" "b"] [@comp "a" "c"]]}))
(is (= (:will-update @res)
@ -762,6 +764,111 @@
(is (= (:will-unmount @res)
{:at 10 :args [@t]})))))
(deftest lifecycle-native
(let [n1 (atom 0)
t (atom 0)
res (atom {})
oldprops (atom nil)
newprops (atom nil)
add-args (fn [key args]
(swap! res assoc key
{:at (swap! n1 inc)
:args (vec args)}))
render (fn [& args]
(this-as
c
(when @newprops
(is (= @newprops) (first args))
(is (= @newprops) (r/props c)))
(is (= c (r/current-component)))
(is (= (first args) (r/props c)))
(add-args :render
{:children (r/children c)})
[:div (first args)]))
ls {:get-initial-state
(fn [& args]
(reset! t (first args))
(reset! oldprops (-> args first r/props))
(add-args :initial-state args)
{:foo "bar"})
:component-will-mount
(fn [& args]
(this-as c (is (= c (first args))))
(add-args :will-mount args))
:component-did-mount
(fn [& args]
(this-as c (is (= c (first args))))
(add-args :did-mount args))
:should-component-update
(fn [& args]
(this-as c (is (= c (first args))))
(add-args :should-update args) true)
:component-will-receive-props
(fn [& args]
(reset! newprops (-> args second second))
(this-as c
(is (= c (first args)))
(add-args :will-receive (into [(dissoc (r/props c) :children)]
(:children (r/props c))))))
:component-will-update
(fn [& args]
(this-as c (is (= c (first args))))
(add-args :will-update args))
:component-did-update
(fn [& args]
(this-as c (is (= c (first args))))
(add-args :did-update args))
:component-will-unmount
(fn [& args]
(this-as c (is (= c (first args))))
(add-args :will-unmount args))}
c1 (r/create-class
(assoc ls :reagent-render render))
defarg [{:foo "bar"} "a" "b"]
arg (r/atom defarg)
comp (atom c1)
cnative (fn []
(into [:> @comp] @arg))
check (fn []
(is (= (:initial-state @res)
{:at 1 :args [@t]}))
(is (= (:will-mount @res)
{:at 2 :args [@t]}))
(is (= (:render @res)
{:at 3 :args [[:children ["a" "b"]]]}))
(is (= (:did-mount @res)
{:at 4 :args [@t]}))
(reset! arg [{:f "oo"} "a" "c"])
(r/flush)
(is (= (:will-receive @res)
{:at 5 :args [{:foo "bar"} "a" "b"]}))
(let [a (:should-update @res)
{at :at
[this oldv newv] :args} a]
(is (= at 6))
(is (= (count (:args a)) 3))
(is (= (js->clj oldv) (js->clj [@t @oldprops])))
(is (= newv [@t @newprops])))
(let [a (:will-update @res)
{at :at
[this newv] :args} a]
(is (= at 7))
(is (= newv [@t @newprops])))
(is (= (:render @res)
{:at 8 :args [[:children ["a" "c"]]]}))
(let [a (:did-update @res)
{at :at
[this oldv] :args} a]
(is (= at 9))
(is (= oldv [@t @oldprops]))))]
(when isClient
(with-mounted-component [cnative] check)
(is (= (:will-unmount @res)
{:at 10 :args [@t]})))))
(defn foo []
[:div])
@ -817,7 +924,7 @@
(is (= e
{:error (lstr "Error rendering component (" stack1 ")")})))
(let [e (debug/track-warnings #(r/reactify-component nat))]
(let [e (debug/track-warnings #(r/as-element [nat]))]
(is (re-find #"Using native React classes directly"
(-> e :warn first))))