mirror of https://github.com/status-im/reagent.git
Make functional Reagent components optional, using new opts parameter
Render/as-element and other functions now take additional parameter for setting options. Currently only option is :functional-reag-elements?, if set to true, by default Reagent components are created as Functions, which behave similarly to Classes, but allow using Hooks.
This commit is contained in:
parent
63e118d2a0
commit
22a2841b45
|
@ -46,8 +46,8 @@
|
||||||
(defn as-element
|
(defn as-element
|
||||||
"Turns a vector of Hiccup syntax into a React element. Returns form
|
"Turns a vector of Hiccup syntax into a React element. Returns form
|
||||||
unchanged if it is not a vector."
|
unchanged if it is not a vector."
|
||||||
[form]
|
([form] (tmpl/as-element form nil))
|
||||||
(tmpl/as-element form))
|
([form opts] (tmpl/as-element form opts)))
|
||||||
|
|
||||||
(defn adapt-react-class
|
(defn adapt-react-class
|
||||||
"Returns an adapter for a native React class, that may be used
|
"Returns an adapter for a native React class, that may be used
|
||||||
|
@ -60,9 +60,10 @@
|
||||||
"Returns an adapter for a Reagent component, that may be used from
|
"Returns an adapter for a Reagent component, that may be used from
|
||||||
React, for example in JSX. A single argument, props, is passed to
|
React, for example in JSX. A single argument, props, is passed to
|
||||||
the component, converted to a map."
|
the component, converted to a map."
|
||||||
[c]
|
([c] (reactify-component c nil))
|
||||||
(assert-some c "Component")
|
([c opts]
|
||||||
(comp/reactify-component c))
|
(assert-some c "Component")
|
||||||
|
(comp/reactify-component c opts)))
|
||||||
|
|
||||||
(defn render
|
(defn render
|
||||||
"Render a Reagent component into the DOM. The first argument may be
|
"Render a Reagent component into the DOM. The first argument may be
|
||||||
|
|
|
@ -34,10 +34,13 @@
|
||||||
Returns the mounted component instance."
|
Returns the mounted component instance."
|
||||||
([comp container]
|
([comp container]
|
||||||
(render comp container nil))
|
(render comp container nil))
|
||||||
([comp container callback]
|
([comp container callback-or-opts]
|
||||||
(ratom/flush!)
|
(ratom/flush!)
|
||||||
(let [f (fn []
|
(let [[opts callback] (if (fn? callback-or-opts)
|
||||||
(tmpl/as-element (if (fn? comp) (comp) comp)))]
|
[nil callback-or-opts]
|
||||||
|
[callback-or-opts (:callback callback-or-opts)])
|
||||||
|
f (fn []
|
||||||
|
(tmpl/as-element (if (fn? comp) (comp) comp) opts))]
|
||||||
(render-comp f container callback))))
|
(render-comp f container callback))))
|
||||||
|
|
||||||
(defn unmount-component-at-node
|
(defn unmount-component-at-node
|
||||||
|
|
|
@ -6,14 +6,18 @@
|
||||||
|
|
||||||
(defn render-to-string
|
(defn render-to-string
|
||||||
"Turns a component into an HTML string."
|
"Turns a component into an HTML string."
|
||||||
[component]
|
([component]
|
||||||
(ratom/flush!)
|
(render-to-string component nil))
|
||||||
(binding [util/*non-reactive* true]
|
([component opts]
|
||||||
(dom-server/renderToString (tmpl/as-element component))))
|
(ratom/flush!)
|
||||||
|
(binding [util/*non-reactive* true]
|
||||||
|
(dom-server/renderToString (tmpl/as-element component opts)))))
|
||||||
|
|
||||||
(defn render-to-static-markup
|
(defn render-to-static-markup
|
||||||
"Turns a component into an HTML string, without data-react-id attributes, etc."
|
"Turns a component into an HTML string, without data-react-id attributes, etc."
|
||||||
[component]
|
([component]
|
||||||
(ratom/flush!)
|
(render-to-static-markup component nil))
|
||||||
|
([component opts]
|
||||||
|
(ratom/flush!)
|
||||||
(binding [util/*non-reactive* true]
|
(binding [util/*non-reactive* true]
|
||||||
(dom-server/renderToStaticMarkup (tmpl/as-element component))))
|
(dom-server/renderToStaticMarkup (tmpl/as-element component opts)))))
|
||||||
|
|
|
@ -111,10 +111,11 @@
|
||||||
5 (.call f c (nth v 1) (nth v 2) (nth v 3) (nth v 4))
|
5 (.call f c (nth v 1) (nth v 2) (nth v 3) (nth v 4))
|
||||||
(.apply f c (.slice (into-array v) 1)))))]
|
(.apply f c (.slice (into-array v) 1)))))]
|
||||||
(cond
|
(cond
|
||||||
(vector? res) (as-element res)
|
;; FIXME: Opts
|
||||||
|
(vector? res) (as-element res nil)
|
||||||
(ifn? res) (let [f (if (reagent-class? res)
|
(ifn? res) (let [f (if (reagent-class? res)
|
||||||
(fn [& args]
|
(fn [& args]
|
||||||
(as-element (apply vector res args)))
|
(as-element (apply vector res args) nil))
|
||||||
res)]
|
res)]
|
||||||
(set! (.-reagentRender c) f)
|
(set! (.-reagentRender c) f)
|
||||||
(recur c))
|
(recur c))
|
||||||
|
@ -373,7 +374,7 @@
|
||||||
|
|
||||||
cmp))
|
cmp))
|
||||||
|
|
||||||
(defn fn-to-class [f]
|
(defn fn-to-class [f opts]
|
||||||
(assert-callable f)
|
(assert-callable f)
|
||||||
(warn-unless (not (and (react-class? f)
|
(warn-unless (not (and (react-class? f)
|
||||||
(not (reagent-class? f))))
|
(not (reagent-class? f))))
|
||||||
|
@ -389,15 +390,16 @@
|
||||||
res (create-class withrender)]
|
res (create-class withrender)]
|
||||||
(cache-react-class f res))))
|
(cache-react-class f res))))
|
||||||
|
|
||||||
(defn as-class [tag]
|
(defn as-class [tag opts]
|
||||||
|
;; TODO: Cache per opts
|
||||||
(if-some [cached-class (cached-react-class tag)]
|
(if-some [cached-class (cached-react-class tag)]
|
||||||
cached-class
|
cached-class
|
||||||
(fn-to-class tag)))
|
(fn-to-class tag opts)))
|
||||||
|
|
||||||
(defn reactify-component [comp]
|
(defn reactify-component [comp opts]
|
||||||
(if (react-class? comp)
|
(if (react-class? comp)
|
||||||
comp
|
comp
|
||||||
(as-class comp)))
|
(as-class comp opts)))
|
||||||
|
|
||||||
(defn functional-wrap-render
|
(defn functional-wrap-render
|
||||||
[c]
|
[c]
|
||||||
|
@ -406,10 +408,10 @@
|
||||||
argv (.-argv c)
|
argv (.-argv c)
|
||||||
res (apply f argv)]
|
res (apply f argv)]
|
||||||
(cond
|
(cond
|
||||||
(vector? res) (as-element res)
|
(vector? res) (as-element res (.-opts c))
|
||||||
(ifn? res) (let [f (if (reagent-class? res)
|
(ifn? res) (let [f (if (reagent-class? res)
|
||||||
(fn [& args]
|
(fn [& args]
|
||||||
(as-element (apply vector res args)))
|
(as-element (apply vector res args) (.-opts c)))
|
||||||
res)]
|
res)]
|
||||||
(set! (.-reagentRender c) f)
|
(set! (.-reagentRender c) f)
|
||||||
(recur c))
|
(recur c))
|
||||||
|
@ -501,6 +503,7 @@
|
||||||
original Reagent component."
|
original Reagent component."
|
||||||
[tag]
|
[tag]
|
||||||
;; TODO: Could be disabled for optimized builds?
|
;; TODO: Could be disabled for optimized builds?
|
||||||
|
;; TODO: Need to cache per opts?
|
||||||
(or (gobj/get fun-components tag)
|
(or (gobj/get fun-components tag)
|
||||||
(let [f (fn [jsprops] (functional-render jsprops))
|
(let [f (fn [jsprops] (functional-render jsprops))
|
||||||
_ (set! (.-displayName f) (util/fun-name tag))
|
_ (set! (.-displayName f) (util/fun-name tag))
|
||||||
|
|
|
@ -246,10 +246,10 @@
|
||||||
:component-did-update input-component-set-value
|
:component-did-update input-component-set-value
|
||||||
:component-will-unmount input-unmount
|
:component-will-unmount input-unmount
|
||||||
:reagent-render
|
:reagent-render
|
||||||
(fn [argv component jsprops first-child]
|
(fn [argv component jsprops first-child opts]
|
||||||
(let [this comp/*current-component*]
|
(let [this comp/*current-component*]
|
||||||
(input-render-setup this jsprops)
|
(input-render-setup this jsprops)
|
||||||
(make-element argv component jsprops first-child)))})
|
(make-element argv component jsprops first-child opts)))})
|
||||||
|
|
||||||
(defn reagent-input
|
(defn reagent-input
|
||||||
[]
|
[]
|
||||||
|
@ -293,13 +293,21 @@
|
||||||
(if (= :> (nth v 0 nil))
|
(if (= :> (nth v 0 nil))
|
||||||
(get-key (nth v 2 nil))))))
|
(get-key (nth v 2 nil))))))
|
||||||
|
|
||||||
(defn reag-element [tag v]
|
(defn reag-element [tag v opts]
|
||||||
|
(let [c (comp/as-class tag opts)
|
||||||
|
jsprops #js {}]
|
||||||
|
(set! (.-argv jsprops) v)
|
||||||
|
(when-some [key (key-from-vec v)]
|
||||||
|
(set! (.-key jsprops) key))
|
||||||
|
(react/createElement c jsprops)))
|
||||||
|
|
||||||
|
(defn functional-reag-element [tag v opts]
|
||||||
(if (or (comp/react-class? tag)
|
(if (or (comp/react-class? tag)
|
||||||
;; TODO: Should check others for real comptibility, this fixes tests
|
;; TODO: Should check others for real comptibility, this fixes tests
|
||||||
;; TODO: Drop support for fn + meta for Class component methods?
|
;; TODO: Drop support for fn + meta for Class component methods?
|
||||||
(:should-component-update (meta tag)))
|
(:should-component-update (meta tag)))
|
||||||
;; as-class unncessary later as tag is always class
|
;; as-class unncessary later as tag is always class
|
||||||
(let [c (comp/as-class tag)
|
(let [c (comp/as-class tag opts)
|
||||||
jsprops #js {}]
|
jsprops #js {}]
|
||||||
(set! (.-argv jsprops) v)
|
(set! (.-argv jsprops) v)
|
||||||
(when-some [key (key-from-vec v)]
|
(when-some [key (key-from-vec v)]
|
||||||
|
@ -308,6 +316,7 @@
|
||||||
(let [jsprops #js {}]
|
(let [jsprops #js {}]
|
||||||
(set! (.-reagentRender jsprops) tag)
|
(set! (.-reagentRender jsprops) tag)
|
||||||
(set! (.-argv jsprops) (subvec v 1))
|
(set! (.-argv jsprops) (subvec v 1))
|
||||||
|
(set! (.-opts jsprops) opts)
|
||||||
(when-some [key (key-from-vec v)]
|
(when-some [key (key-from-vec v)]
|
||||||
(set! (.-key jsprops) key))
|
(set! (.-key jsprops) key))
|
||||||
(react/createElement (comp/funtional-render-fn tag) jsprops))))
|
(react/createElement (comp/funtional-render-fn tag) jsprops))))
|
||||||
|
@ -335,7 +344,7 @@
|
||||||
(gobj/set tag-name-cache x v)
|
(gobj/set tag-name-cache x v)
|
||||||
v)))
|
v)))
|
||||||
|
|
||||||
(defn native-element [parsed argv first]
|
(defn native-element [parsed argv first opts]
|
||||||
(let [component (.-tag parsed)
|
(let [component (.-tag parsed)
|
||||||
props (nth argv first nil)
|
props (nth argv first nil)
|
||||||
hasprops (or (nil? props) (map? props))
|
hasprops (or (nil? props) (map? props))
|
||||||
|
@ -343,13 +352,13 @@
|
||||||
#js {})
|
#js {})
|
||||||
first-child (+ first (if hasprops 1 0))]
|
first-child (+ first (if hasprops 1 0))]
|
||||||
(if (input-component? component)
|
(if (input-component? component)
|
||||||
(-> [(reagent-input) argv component jsprops first-child]
|
(-> [(reagent-input) argv component jsprops first-child opts]
|
||||||
(with-meta (meta argv))
|
(with-meta (meta argv))
|
||||||
as-element)
|
(as-element opts))
|
||||||
(do
|
(do
|
||||||
(when-some [key (-> (meta argv) get-key)]
|
(when-some [key (-> (meta argv) get-key)]
|
||||||
(set! (.-key jsprops) key))
|
(set! (.-key jsprops) key))
|
||||||
(make-element argv component jsprops first-child)))))
|
(make-element argv component jsprops first-child opts)))))
|
||||||
|
|
||||||
(defn str-coll [coll]
|
(defn str-coll [coll]
|
||||||
(if (dev?)
|
(if (dev?)
|
||||||
|
@ -365,7 +374,7 @@
|
||||||
(defn hiccup-err [v & msg]
|
(defn hiccup-err [v & msg]
|
||||||
(str (apply str msg) ": " (str-coll v) "\n" (comp/comp-name)))
|
(str (apply str msg) ": " (str-coll v) "\n" (comp/comp-name)))
|
||||||
|
|
||||||
(defn vec-to-elem [v]
|
(defn vec-to-elem [v opts]
|
||||||
(assert (pos? (count v)) (hiccup-err v "Hiccup form should not be empty"))
|
(assert (pos? (count v)) (hiccup-err v "Hiccup form should not be empty"))
|
||||||
(let [tag (nth v 0 nil)]
|
(let [tag (nth v 0 nil)]
|
||||||
(assert (valid-tag? tag) (hiccup-err v "Invalid Hiccup form"))
|
(assert (valid-tag? tag) (hiccup-err v "Invalid Hiccup form"))
|
||||||
|
@ -377,53 +386,56 @@
|
||||||
(let [n (name tag)
|
(let [n (name tag)
|
||||||
pos (.indexOf n ">")]
|
pos (.indexOf n ">")]
|
||||||
(case pos
|
(case pos
|
||||||
-1 (native-element (cached-parse n) v 1)
|
-1 (native-element (cached-parse n) v 1 opts)
|
||||||
0 (let [component (nth v 1 nil)]
|
0 (let [component (nth v 1 nil)]
|
||||||
;; Support [:> component ...]
|
;; Support [:> component ...]
|
||||||
(assert (= ">" n) (hiccup-err v "Invalid Hiccup tag"))
|
(assert (= ">" n) (hiccup-err v "Invalid Hiccup tag"))
|
||||||
(native-element (->HiccupTag component nil nil nil) v 2))
|
(native-element (->HiccupTag component nil nil nil) v 2 opts))
|
||||||
;; Support extended hiccup syntax, i.e :div.bar>a.foo
|
;; Support extended hiccup syntax, i.e :div.bar>a.foo
|
||||||
;; Apply metadata (e.g. :key) to the outermost element.
|
;; Apply metadata (e.g. :key) to the outermost element.
|
||||||
;; Metadata is probably used only with sequeneces, and in that case
|
;; Metadata is probably used only with sequeneces, and in that case
|
||||||
;; only the key of the outermost element matters.
|
;; only the key of the outermost element matters.
|
||||||
(recur (with-meta [(subs n 0 pos)
|
(recur (with-meta [(subs n 0 pos)
|
||||||
(assoc (with-meta v nil) 0 (subs n (inc pos)))]
|
(assoc (with-meta v nil) 0 (subs n (inc pos)))]
|
||||||
(meta v)))))
|
(meta v))
|
||||||
|
opts)))
|
||||||
|
|
||||||
(instance? NativeWrapper tag)
|
(instance? NativeWrapper tag)
|
||||||
(native-element tag v 1)
|
(native-element tag v 1 opts)
|
||||||
|
|
||||||
:else (reag-element tag v))))
|
:else (if (:functional-reag-elements? opts)
|
||||||
|
(functional-reag-element tag v opts)
|
||||||
|
(reag-element tag v opts)))))
|
||||||
|
|
||||||
(declare expand-seq)
|
(declare expand-seq)
|
||||||
(declare expand-seq-check)
|
(declare expand-seq-check)
|
||||||
|
|
||||||
(defn as-element [x]
|
(defn as-element [x opts]
|
||||||
(cond (js-val? x) x
|
(cond (js-val? x) x
|
||||||
(vector? x) (vec-to-elem x)
|
(vector? x) (vec-to-elem x opts)
|
||||||
(seq? x) (if (dev?)
|
(seq? x) (if (dev?)
|
||||||
(expand-seq-check x)
|
(expand-seq-check x opts)
|
||||||
(expand-seq x))
|
(expand-seq x opts))
|
||||||
(named? x) (name x)
|
(named? x) (name x)
|
||||||
(satisfies? IPrintWithWriter x) (pr-str x)
|
(satisfies? IPrintWithWriter x) (pr-str x)
|
||||||
:else x))
|
:else x))
|
||||||
|
|
||||||
(set! comp/as-element as-element)
|
(set! comp/as-element as-element)
|
||||||
|
|
||||||
(defn expand-seq [s]
|
(defn expand-seq [s opts]
|
||||||
(into-array (map as-element s)))
|
(into-array (map as-element s)))
|
||||||
|
|
||||||
(defn expand-seq-dev [s ^clj o]
|
(defn expand-seq-dev [s ^clj o opts]
|
||||||
(into-array (map (fn [val]
|
(into-array (map (fn [val]
|
||||||
(when (and (vector? val)
|
(when (and (vector? val)
|
||||||
(nil? (key-from-vec val)))
|
(nil? (key-from-vec val)))
|
||||||
(set! (.-no-key o) true))
|
(set! (.-no-key o) true))
|
||||||
(as-element val))
|
(as-element val opts))
|
||||||
s)))
|
s)))
|
||||||
|
|
||||||
(defn expand-seq-check [x]
|
(defn expand-seq-check [x opts]
|
||||||
(let [ctx #js{}
|
(let [ctx #js{}
|
||||||
[res derefed] (ratom/check-derefs #(expand-seq-dev x ctx))]
|
[res derefed] (ratom/check-derefs #(expand-seq-dev x ctx opts))]
|
||||||
(when derefed
|
(when derefed
|
||||||
(warn (hiccup-err x "Reactive deref not supported in lazy seq, "
|
(warn (hiccup-err x "Reactive deref not supported in lazy seq, "
|
||||||
"it should be wrapped in doall")))
|
"it should be wrapped in doall")))
|
||||||
|
@ -431,17 +443,17 @@
|
||||||
(warn (hiccup-err x "Every element in a seq should have a unique :key")))
|
(warn (hiccup-err x "Every element in a seq should have a unique :key")))
|
||||||
res))
|
res))
|
||||||
|
|
||||||
(defn make-element [argv component jsprops first-child]
|
(defn make-element [argv component jsprops first-child opts]
|
||||||
(case (- (count argv) first-child)
|
(case (- (count argv) first-child)
|
||||||
;; Optimize cases of zero or one child
|
;; Optimize cases of zero or one child
|
||||||
0 (react/createElement component jsprops)
|
0 (react/createElement component jsprops)
|
||||||
|
|
||||||
1 (react/createElement component jsprops
|
1 (react/createElement component jsprops
|
||||||
(as-element (nth argv first-child nil)))
|
(as-element (nth argv first-child nil) opts))
|
||||||
|
|
||||||
(.apply react/createElement nil
|
(.apply react/createElement nil
|
||||||
(reduce-kv (fn [a k v]
|
(reduce-kv (fn [a k v]
|
||||||
(when (>= k first-child)
|
(when (>= k first-child)
|
||||||
(.push a (as-element v)))
|
(.push a (as-element v opts)))
|
||||||
a)
|
a)
|
||||||
#js[component jsprops] argv))))
|
#js[component jsprops] argv))))
|
||||||
|
|
|
@ -10,14 +10,14 @@
|
||||||
(js/performance.mark "functional-start")
|
(js/performance.mark "functional-start")
|
||||||
; (simple-benchmark [x [hello-world-component]] (tmpl/vec-to-elem x) 100000)
|
; (simple-benchmark [x [hello-world-component]] (tmpl/vec-to-elem x) 100000)
|
||||||
(dotimes [i 100000]
|
(dotimes [i 100000]
|
||||||
(tmpl/vec-to-elem [hello-world-component]))
|
(tmpl/vec-to-elem [hello-world-component] nil))
|
||||||
(js/performance.mark "functional-end")
|
(js/performance.mark "functional-end")
|
||||||
(js/performance.measure "functional" "functional-start" "functional-end")
|
(js/performance.measure "functional" "functional-start" "functional-end")
|
||||||
|
|
||||||
(js/performance.mark "class-start")
|
(js/performance.mark "class-start")
|
||||||
; (simple-benchmark [x [^:class-component hello-world-component]] (tmpl/vec-to-elem x) 100000)
|
; (simple-benchmark [x [^:class-component hello-world-component]] (tmpl/vec-to-elem x) 100000)
|
||||||
(dotimes [i 100000]
|
(dotimes [i 100000]
|
||||||
(tmpl/vec-to-elem [^:class-component hello-world-component]))
|
(tmpl/vec-to-elem [^:class-component hello-world-component] nil))
|
||||||
(js/performance.mark "class-end")
|
(js/performance.mark "class-end")
|
||||||
(js/performance.measure "class" "class-start" "class-end")
|
(js/performance.measure "class" "class-start" "class-end")
|
||||||
)
|
)
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,15 +2,18 @@
|
||||||
(:require [reagent.core :as r]
|
(:require [reagent.core :as r]
|
||||||
[reagent.dom :as rdom]))
|
[reagent.dom :as rdom]))
|
||||||
|
|
||||||
(defn with-mounted-component [comp f]
|
(defn with-mounted-component
|
||||||
(when r/is-client
|
([comp f]
|
||||||
(let [div (.createElement js/document "div")]
|
(with-mounted-component comp nil f))
|
||||||
(try
|
([comp opts f]
|
||||||
(let [c (rdom/render comp div)]
|
(when r/is-client
|
||||||
(f c div))
|
(let [div (.createElement js/document "div")]
|
||||||
(finally
|
(try
|
||||||
(rdom/unmount-component-at-node div)
|
(let [c (rdom/render comp div opts)]
|
||||||
(r/flush))))))
|
(f c div))
|
||||||
|
(finally
|
||||||
|
(rdom/unmount-component-at-node div)
|
||||||
|
(r/flush)))))))
|
||||||
|
|
||||||
(defn with-mounted-component-async [comp done f]
|
(defn with-mounted-component-async [comp done f]
|
||||||
(when r/is-client
|
(when r/is-client
|
||||||
|
|
Loading…
Reference in New Issue