mirror of
https://github.com/logseq/logseq.git
synced 2026-05-27 14:14:24 +00:00
test(react-impl): add react-impl
This commit is contained in:
95
src/test/frontend/react_impl.cljc
Normal file
95
src/test/frontend/react_impl.cljc
Normal file
@@ -0,0 +1,95 @@
|
||||
(ns frontend.react-impl
|
||||
"Note: don't run component parallel"
|
||||
;;#?(:clj (:require [clojure.tools.macro :refer [name-with-attributes]]))
|
||||
#?(:cljs (:require-macros [frontend.react-impl])))
|
||||
|
||||
(defn name-with-attributes
|
||||
"copy: https://github.com/clojure/tools.macro/blob/master/src/main/clojure/clojure/tools/macro.clj#L282"
|
||||
[name macro-args]
|
||||
(let [[docstring macro-args] (if (string? (first macro-args))
|
||||
[(first macro-args) (next macro-args)]
|
||||
[nil macro-args])
|
||||
[attr macro-args] (if (map? (first macro-args))
|
||||
[(first macro-args) (next macro-args)]
|
||||
[{} macro-args])
|
||||
attr (if docstring
|
||||
(assoc attr :doc docstring)
|
||||
attr)
|
||||
attr (if (meta name)
|
||||
(conj (meta name) attr)
|
||||
attr)]
|
||||
[(with-meta name attr) macro-args]))
|
||||
|
||||
#_{:component-key {:result nil
|
||||
:watches []
|
||||
:f-path nil
|
||||
:f nil}}
|
||||
(def react-defines (atom {}))
|
||||
(def ^:dynamic *f-indent* nil)
|
||||
(def ^:dynamic *react-f-path* '())
|
||||
(def ^:dynamic *from-watching-fn* nil)
|
||||
|
||||
(defn react'
|
||||
[react-ref]
|
||||
(let [ident *f-indent*
|
||||
f (get-in @react-defines [ident :f])]
|
||||
(cond
|
||||
(= true *from-watching-fn*)
|
||||
(deref react-ref)
|
||||
|
||||
(ifn? f)
|
||||
(let [component (get @react-defines ident)]
|
||||
(when-not ((:watches component) react-ref)
|
||||
(let [new-component (update component :watches conj react-ref)]
|
||||
(add-watch react-ref react-ref
|
||||
|
||||
(fn [& _]
|
||||
(binding [*from-watching-fn* true]
|
||||
(reset! (:result component) (f))
|
||||
(let [f-path (rest (get-in @react-defines [ident :f-path]))]
|
||||
(doseq [{:keys [f ident]} f-path]
|
||||
(binding [*f-indent* ident]
|
||||
(let [component (get @react-defines ident)]
|
||||
(reset! (:result component) (f)))))))))
|
||||
(swap! react-defines assoc *f-indent* new-component)
|
||||
@react-ref)))
|
||||
|
||||
:else (deref react-ref))))
|
||||
|
||||
(defn react-fn
|
||||
[f]
|
||||
(let [result-ref (atom nil)
|
||||
ident (keyword (gensym))]
|
||||
(binding [*f-indent* ident
|
||||
*react-f-path* (conj *react-f-path* {:f f :ident ident})]
|
||||
(swap! react-defines assoc *f-indent* {:result result-ref
|
||||
:watches #{}
|
||||
:f-path *react-f-path*
|
||||
:f f})
|
||||
(reset! result-ref (f))
|
||||
(deref (get-in @react-defines [ident :result])))))
|
||||
|
||||
#?(:clj (defmacro react
|
||||
[react-ref]
|
||||
`(react' ~react-ref)))
|
||||
|
||||
#?(:clj (defmacro defc
|
||||
[sym args & body]
|
||||
`(defn ~sym ~args
|
||||
(fn []
|
||||
(let [f# (fn [] ~@body)]
|
||||
(react-fn f#))))))
|
||||
|
||||
#?(:clj (defmacro auto-clean-state
|
||||
[& body]
|
||||
`(do (reset! react-defines {})
|
||||
(let [result# ~@body]
|
||||
(reset! react-defines {})
|
||||
result#))))
|
||||
|
||||
(comment
|
||||
(def b (atom 1))
|
||||
|
||||
(defc inner
|
||||
[c]
|
||||
(+ c (react b))))
|
||||
@@ -1,60 +1,60 @@
|
||||
(ns frontend.react-impl-test
|
||||
"To facilitate testing, imitate the behavior of react")
|
||||
"To facilitate testing, imitate the behavior of react"
|
||||
(:require [frontend.react-impl :as r]
|
||||
[cljs.test :refer [deftest is are testing use-fixtures]]))
|
||||
|
||||
;{:component-key {:result nil
|
||||
; :watches []}}
|
||||
(def react-defines (atom {}))
|
||||
(def ^:dynamic *react-fn* nil)
|
||||
(deftest simple-react-test
|
||||
(r/auto-clean-state
|
||||
(let [react-ref (atom 1)]
|
||||
|
||||
(defn react
|
||||
[react-ref]
|
||||
(let [f *react-fn*]
|
||||
(cond
|
||||
(= :from-watching-fn f)
|
||||
@react-ref
|
||||
(r/defc simple-component
|
||||
[]
|
||||
(+ 2 (r/react react-ref)))
|
||||
|
||||
(ifn? f)
|
||||
(let [component (get @react-defines f)]
|
||||
(when-not ((:watches component) react-ref)
|
||||
(let [new-component (update component :watches conj react-ref)]
|
||||
(add-watch react-ref react-ref
|
||||
(fn [& _]
|
||||
(binding [*react-fn* :from-watching-fn]
|
||||
(reset! (:result component) (f)))))
|
||||
(swap! react-defines assoc f new-component)
|
||||
@react-ref)))
|
||||
(let [get-result (simple-component)]
|
||||
|
||||
:else (deref react-ref))))
|
||||
(is (= 3 (get-result)))
|
||||
(reset! react-ref 2)
|
||||
(is (= 4 (get-result)))))))
|
||||
|
||||
(defn react-fn
|
||||
[f]
|
||||
{:pre [(fn? f)]}
|
||||
(let [result-ref (atom nil)
|
||||
;; Each react-fn invoke will generate new *react-fn*, though the same f.
|
||||
f' (fn [] (f))]
|
||||
(binding [*react-fn* f']
|
||||
(swap! react-defines assoc f' {:result result-ref
|
||||
:watches #{}})
|
||||
(reset! result-ref (f'))
|
||||
{:clear-state-fn
|
||||
(fn [] (-> (swap! react-defines dissoc f')
|
||||
(empty?)))
|
||||
:get-value-fn
|
||||
(fn [] (deref (get-in @react-defines [f' :result])))})))
|
||||
(deftest nest-component-test
|
||||
(r/auto-clean-state
|
||||
(let [a (atom 1)
|
||||
b (atom 2)]
|
||||
|
||||
(defn clear-react-resources
|
||||
[]
|
||||
(reset! react-defines nil))
|
||||
(r/defc inner
|
||||
[]
|
||||
(r/react b))
|
||||
|
||||
(comment
|
||||
(let [react-ref (atom 1)
|
||||
f (fn []
|
||||
(let [haha (react react-ref)]
|
||||
(* haha 2)))
|
||||
{:keys [clear-state-fn get-value-fn]} (react-fn f)]
|
||||
(prn (get-value-fn))
|
||||
(reset! react-ref 2)
|
||||
(prn (get-value-fn))
|
||||
(clear-state-fn)))
|
||||
(r/defc out
|
||||
[]
|
||||
(let [out (r/react a)
|
||||
get-inner-result (inner)]
|
||||
(+ out (get-inner-result))))
|
||||
|
||||
(let [get-out-result (out)]
|
||||
(is (= 3 (get-out-result)))
|
||||
(reset! b 4)
|
||||
(is (= 5 (get-out-result)))))))
|
||||
|
||||
(deftest defc-params-test
|
||||
(r/auto-clean-state
|
||||
(let [a (atom 1)
|
||||
b (atom 2)]
|
||||
|
||||
(r/defc inner-1
|
||||
[c]
|
||||
(+ c (r/react b)))
|
||||
|
||||
(r/defc out-1
|
||||
[]
|
||||
(let [out (r/react a)
|
||||
get-inner-result (inner-1 5)]
|
||||
(+ out (get-inner-result))))
|
||||
|
||||
(let [get-out-result (out-1)]
|
||||
(is (= 8 (get-out-result)))
|
||||
|
||||
(reset! b 4)
|
||||
|
||||
(is (= 10 (get-out-result)))))))
|
||||
Reference in New Issue
Block a user