mirror of https://github.com/status-im/reagent.git
Test creating functional components
This commit is contained in:
parent
c70f4f9c4e
commit
f57b39aa31
|
@ -431,3 +431,64 @@
|
|||
(if (react-class? comp)
|
||||
comp
|
||||
(as-class comp)))
|
||||
|
||||
(defonce fun-component-state #js {})
|
||||
|
||||
(defn functional-render [jsprops]
|
||||
(let [argv (.-argv jsprops)
|
||||
tag (.-tag jsprops)
|
||||
res (if util/*non-reactive*
|
||||
(apply tag argv)
|
||||
;; Create persistent ID for each rendered functional component,
|
||||
;; this is used to store internal Reagent state, like render
|
||||
;; reaction etc. in a separate store where changes doesn't
|
||||
;; trigger render.
|
||||
(let [[id _] (react/useState (js/Symbol))
|
||||
[_ update-count] (react/useState 0)
|
||||
reagent-state (or (gobj/get fun-component-state id)
|
||||
;; TODO: Mock state atom?
|
||||
(let [obj #js {:forceUpdate (fn [] (update-count inc))
|
||||
:cljsMountOrder (batch/next-mount-count)}]
|
||||
(gobj/set fun-component-state id obj)
|
||||
obj))]
|
||||
|
||||
(react/useEffect
|
||||
(fn mount []
|
||||
(fn unmount []
|
||||
(some-> (.-cljsRatom reagent-state) ratom/dispose!)
|
||||
(gobj/remove fun-component-state id)))
|
||||
;; Only run effect once on mount and unmount
|
||||
#js [])
|
||||
|
||||
;; Note: it might be possible to mock some React Component
|
||||
;; methods in the object and use it as *current-component*
|
||||
|
||||
;; TODO: If return value is ifn?, consider form-2 component.
|
||||
|
||||
(assert-callable tag)
|
||||
|
||||
(batch/mark-rendered reagent-state)
|
||||
|
||||
;; static-fns :render
|
||||
(if-let [rat (.-cljsRatom reagent-state)]
|
||||
(._run rat false)
|
||||
(ratom/run-in-reaction
|
||||
;; Mock Class component API
|
||||
#(binding [*current-component* reagent-state]
|
||||
(apply tag argv))
|
||||
reagent-state
|
||||
"cljsRatom"
|
||||
batch/queue-render
|
||||
rat-opts))))]
|
||||
;; do-render
|
||||
;; wrap-render
|
||||
(cond
|
||||
(vector? res) (as-element res)
|
||||
;; FIXME: Support form-2 components???
|
||||
; (ifn? res) (let [f (if (reagent-class? res)
|
||||
; (create-class
|
||||
; {:reagent-render (fn [& args]
|
||||
; (as-element (apply vector res args)))})
|
||||
; res)]
|
||||
; f)
|
||||
:else res)))
|
||||
|
|
|
@ -287,12 +287,27 @@
|
|||
(-> v (nth 1 nil) get-key)))
|
||||
|
||||
(defn reag-element [tag v]
|
||||
(let [c (comp/as-class tag)
|
||||
jsprops #js {}]
|
||||
(set! (.-argv jsprops) v)
|
||||
(when-some [key (key-from-vec v)]
|
||||
(set! (.-key jsprops) key))
|
||||
(react/createElement c jsprops)))
|
||||
(if (or false
|
||||
(comp/react-class? tag)
|
||||
;; FIXME: Probably temporary workaround.
|
||||
(:class-component (meta tag)))
|
||||
;; as-class unncessary later as tag is always class
|
||||
(let [c (comp/as-class tag)
|
||||
jsprops #js {}]
|
||||
(set! (.-argv jsprops) v)
|
||||
(when-some [key (key-from-vec v)]
|
||||
(set! (.-key jsprops) key))
|
||||
(react/createElement c jsprops))
|
||||
(let [jsprops #js {:tag tag
|
||||
:argv (subvec v 1)}]
|
||||
(when-some [key (key-from-vec v)]
|
||||
(set! (.-key jsprops) key))
|
||||
(react/createElement comp/functional-render jsprops))))
|
||||
|
||||
(defn reag-fun-element [v]
|
||||
(let [tag (nth v 1)
|
||||
argv (drop 2 v)]
|
||||
(react/createElement comp/functional-render #js {:tag tag :argv argv})))
|
||||
|
||||
(defn fragment-element [argv]
|
||||
(let [props (nth argv 1 nil)
|
||||
|
@ -355,6 +370,9 @@
|
|||
(keyword-identical? :<> tag)
|
||||
(fragment-element v)
|
||||
|
||||
(keyword-identical? :< tag)
|
||||
(reag-fun-element v)
|
||||
|
||||
(hiccup-tag? tag)
|
||||
(let [n (name tag)
|
||||
pos (.indexOf n ">")]
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
[reagenttest.testtrack]
|
||||
[reagenttest.testwithlet]
|
||||
[reagenttest.testwrap]
|
||||
[reagenttest.testnext]
|
||||
[reagent.impl.template-test]
|
||||
[reagent.impl.util-test]
|
||||
[clojure.test :as test]
|
||||
|
|
|
@ -145,7 +145,7 @@
|
|||
v2 (r/atom 0)
|
||||
c2 (fn [{val :val}]
|
||||
(swap! ran inc)
|
||||
(is (= val @v1))
|
||||
(is (= @v1 val))
|
||||
[:div @v2])
|
||||
c1 (fn []
|
||||
(swap! ran inc)
|
||||
|
@ -175,13 +175,17 @@
|
|||
(deftest init-state-test
|
||||
(when r/is-client
|
||||
(let [ran (r/atom 0)
|
||||
really-simple (fn []
|
||||
(let [this (r/current-component)]
|
||||
(swap! ran inc)
|
||||
(r/set-state this {:foo "foobar"})
|
||||
(fn []
|
||||
[:div (str "this is "
|
||||
(:foo (r/state this)))])))]
|
||||
really-simple
|
||||
;; NOTE: Manually marking the following component as stateful.
|
||||
;; TODO: Should maybe just use create-class here.
|
||||
^:class-component
|
||||
(fn []
|
||||
(let [this (r/current-component)]
|
||||
(swap! ran inc)
|
||||
(r/set-state this {:foo "foobar"})
|
||||
(fn []
|
||||
[:div (str "this is "
|
||||
(:foo (r/state this)))])))]
|
||||
(with-mounted-component [really-simple nil nil]
|
||||
(fn [c div]
|
||||
(swap! ran inc)
|
||||
|
@ -1124,6 +1128,7 @@
|
|||
(r/flush)
|
||||
(is (= 1 @val))
|
||||
(is (= 2 @spy))
|
||||
;; TODO: Functional component can't be force updated from the outside
|
||||
(r/force-update c)
|
||||
(is (= 3 @spy))
|
||||
(r/next-tick #(reset! spy 0))
|
||||
|
@ -1392,3 +1397,67 @@
|
|||
(reset! val 0)
|
||||
(r/flush)
|
||||
(is (= 3 @render)))))))
|
||||
|
||||
;; :< creates functional component for now.
|
||||
;; This is for testing only, hopefully functional component
|
||||
;; can be the default later.
|
||||
(deftest functional-component-poc-simple
|
||||
(when r/is-client
|
||||
(let [c (fn [x]
|
||||
[:span "Hello " x])]
|
||||
(with-mounted-component [:< c "foo"]
|
||||
(fn [c div]
|
||||
(is (nil? c) "Render returns nil for stateless components")
|
||||
(is (= "Hello foo" (.-innerText div))))))))
|
||||
|
||||
(deftest functional-component-poc-state-hook
|
||||
(when r/is-client
|
||||
(let [;; Probably not the best idea to keep
|
||||
;; refernce to state hook update fn, but
|
||||
;; works for testing.
|
||||
set-count! (atom nil)
|
||||
c (fn [x]
|
||||
(let [[c set-count] (react/useState x)]
|
||||
(reset! set-count! set-count)
|
||||
[:span "Count " c]))]
|
||||
(with-mounted-component [:< c 5]
|
||||
(fn [c div]
|
||||
(is (nil? c) "Render returns nil for stateless components")
|
||||
(is (= "Count 5" (.-innerText div)))
|
||||
(@set-count! 6)
|
||||
(is (= "Count 6" (.-innerText div))))))))
|
||||
|
||||
(deftest functional-component-poc-ratom
|
||||
(when r/is-client
|
||||
(let [count (r/atom 5)
|
||||
c (fn [x]
|
||||
[:span "Count " @count])]
|
||||
(with-mounted-component [:< c 5]
|
||||
(fn [c div]
|
||||
(is (nil? c) "Render returns nil for stateless components")
|
||||
(is (= "Count 5" (.-innerText div)))
|
||||
(reset! count 6)
|
||||
(r/flush)
|
||||
(is (= "Count 6" (.-innerText div)))
|
||||
;; TODO: Test that component RAtom is disposed
|
||||
)))))
|
||||
|
||||
|
||||
(deftest functional-component-poc-ratom-state-hook
|
||||
(when r/is-client
|
||||
(let [r-count (r/atom 3)
|
||||
set-count! (atom nil)
|
||||
c (fn [x]
|
||||
(let [[c set-count] (react/useState x)]
|
||||
(reset! set-count! set-count)
|
||||
[:span "Counts " @r-count " " c]))]
|
||||
(with-mounted-component [:< c 15]
|
||||
(fn [c div]
|
||||
(is (nil? c) "Render returns nil for stateless components")
|
||||
(is (= "Counts 3 15" (.-innerText div)))
|
||||
(reset! r-count 6)
|
||||
(r/flush)
|
||||
(is (= "Counts 6 15" (.-innerText div)))
|
||||
(@set-count! 17)
|
||||
(is (= "Counts 6 17" (.-innerText div)))
|
||||
)))))
|
||||
|
|
Loading…
Reference in New Issue