mirror of
https://github.com/logseq/logseq.git
synced 2026-06-02 11:21:22 +00:00
77 lines
2.8 KiB
Clojure
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#))))
|