Files
logseq/src/test/frontend/react.cljc
2022-01-12 00:22:37 +08:00

77 lines
2.8 KiB
Clojure

(ns frontend.react
"To facilitate testing, imitate the behavior of react.
Note: don't run component parallel"
#?(:cljs (:require-macros [frontend.react])))
#_{:component-key {:result nil
:watches []
:root-info nil}}
(def react-components (atom {}))
(def ^:dynamic *with-key* nil)
(def ^:dynamic *comp-key* nil)
(def ^:dynamic *root-info* nil)
(defn react
[react-ref]
(let [comp-key *comp-key*
component (get @react-components comp-key)]
(cond
(some? component)
(do
(when-not ((:watches component) react-ref)
(let [new-component (update component :watches conj react-ref)]
(swap! react-components assoc comp-key new-component)
(add-watch react-ref comp-key
(fn [_key _atom old-state new-state]
(when-not (= old-state new-state)
(let [root-info (get-in @react-components [comp-key :root-info])
{:keys [f comp-key]} root-info]
(binding [*with-key* comp-key
*root-info* root-info]
(let [component (get @react-components comp-key)]
(reset! (:result component) (f))))))))))
@react-ref)
;; Sometime react is not used in component by accident, return the val.
:else
@react-ref)))
(defn set-comp-and-calc-result
[f]
(let [{result :result :as component} (get @react-components *comp-key*)]
(if component
(do (reset! result (f)) result)
(let [result (atom nil)]
(binding [*root-info* (if *root-info* *root-info* {:f f :comp-key *comp-key*})]
(let [component {:result result
:watches #{}
:root-info *root-info*}]
(swap! react-components assoc *comp-key* component))
(reset! result (f))
result)))))
#?(:clj (defmacro defc
[sym args & body]
`(defn ~sym ~args
(assert (some? *with-key*)
"should specify component key by frontend.react-impl/with-key.")
(let [f# (fn []
(binding [*comp-key* *with-key*
;; inner component should specify own *with-key*
*with-key* nil]
~@body))]
(binding [*comp-key* *with-key*]
(set-comp-and-calc-result f#))))))
#?(:clj (defmacro with-key
[key & body]
`(binding [*with-key* ~key]
~@body)))
#?(:clj (defmacro auto-clean-state
[& body]
`(do (reset! react-components {})
(let [result# ~@body]
(reset! react-components {})
result#))))