Handle form-2 and form-3 components

This commit is contained in:
Juho Teperi 2020-02-06 00:06:30 +02:00
parent 9042db423f
commit 678e8adf7b
1 changed files with 59 additions and 50 deletions

View File

@ -402,59 +402,68 @@
(defn functional-render [jsprops] (defn functional-render [jsprops]
(let [argv (.-argv jsprops) (let [argv (.-argv jsprops)
tag (.-tag jsprops) tag (.-tag jsprops)]
res (if util/*non-reactive* (if util/*non-reactive*
(apply tag argv) (let [res (apply tag argv)]
;; Create persistent ID for each rendered functional component, (cond
;; this is used to store internal Reagent state, like render (vector? res) (as-element res)
;; reaction etc. in a separate store where changes doesn't (ifn? res) (let [f (if (reagent-class? res)
;; trigger render. (fn [& args]
(let [[id _] (react/useState (js/Symbol)) (as-element (apply vector res args)))
[_ update-count] (react/useState 0) res)]
reagent-state (or (gobj/get fun-component-state id) (as-element (apply f argv)))
;; TODO: Mock state atom? :else res))
(let [obj #js {:forceUpdate (fn [] (update-count inc)) (let [;; Create persistent ID for each rendered functional component,
:cljsMountOrder (batch/next-mount-count)}] ;; this is used to store internal Reagent state, like render
(gobj/set fun-component-state id obj) ;; reaction etc. in a separate store where changes doesn't
obj))] ;; trigger render.
[id _] (react/useState (js/Symbol))
(react/useEffect ;; Use counter to trigger render manually.
(fn mount [] [_ update-count] (react/useState 0)
(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 ;; This object mimics React Class attributes and methods.
;; methods in the object and use it as *current-component* ;; To support form-2 components, even the render fn needs to
;; be stored as it is created during the first render,
;; and subsequent renders need to retrieve the created fn.
reagent-state (or (gobj/get fun-component-state id)
(let [obj #js {:forceUpdate (fn [] (update-count inc))
:cljsMountOrder (batch/next-mount-count)
:renderFn tag}]
(gobj/set fun-component-state id obj)
obj))]
;; TODO: If return value is ifn?, consider form-2 component. (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 [])
(assert-callable tag) (assert-callable tag)
(batch/mark-rendered reagent-state) (batch/mark-rendered reagent-state)
;; static-fns :render ;; static-fns :render
(if-let [rat (.-cljsRatom reagent-state)] (let [res (if-let [rat (.-cljsRatom reagent-state)]
(._run rat false) (._run rat false)
(ratom/run-in-reaction (ratom/run-in-reaction
;; Mock Class component API ;; Mock Class component API
#(binding [*current-component* reagent-state] #(binding [*current-component* reagent-state]
(apply tag argv)) (apply (.-renderFn reagent-state) argv))
reagent-state reagent-state
"cljsRatom" "cljsRatom"
batch/queue-render batch/queue-render
rat-opts))))] rat-opts))]
;; do-render (cond
;; wrap-render (vector? res) (as-element res)
(cond (ifn? res) (let [;; If original fn returned class (create-class) wrap in fn and call that.
(vector? res) (as-element res) f (if (reagent-class? res)
;; FIXME: Support form-2 components??? (fn [& args]
; (ifn? res) (let [f (if (reagent-class? res) (as-element (apply vector res args)))
; (create-class res)]
; {:reagent-render (fn [& args] ;; Store the returned fn in state, so it will be used in following render calls.
; (as-element (apply vector res args)))}) (set! (.-renderFn reagent-state) f)
; res)] (as-element (apply f argv)))
; f) :else res))))))
:else res)))