From 4526a3602634c2d63c3ae9e1179215bf270778fc Mon Sep 17 00:00:00 2001 From: Vincent Cantin Date: Thu, 11 Sep 2025 10:30:41 +0300 Subject: [PATCH 1/2] Fixes #8: reactive fragment inside a react fragment. --- src/vrac/web.cljc | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/vrac/web.cljc b/src/vrac/web.cljc index c55e986..cb4b16d 100644 --- a/src/vrac/web.cljc +++ b/src/vrac/web.cljc @@ -248,11 +248,27 @@ (reset! old-props props))))))) +#?(:cljs + (defn- collect-dom-nodes! [^js/Array array x] + (cond + (instance? js/Node x) + (.push array x) + + (vector? x) + (doseq [item x] + (collect-dom-nodes! array item)) + + (reactive-fragment? x) + (doseq [item @(:reactive-node x)] + (collect-dom-nodes! array item))))) + + #?(:cljs (defn dynamic-children-effect "Dynamically update the DOM node so that its children keep representing the elements array. - The elements are either js/Element or a reactive node whose value is a sequence of js/Element." - [^js/Element parent-element elements] + The elements are either js/Node (element or text node) or a reactive node whose value is + a vector of js/Node instances." + [^js/Element parent-element nodes] ;; TODO: This algorithm could be improved to only replace children where it is needed. ;; Would it be faster? less CPU-intensive? ;; maybe different algorithms depending on the size? @@ -263,11 +279,7 @@ (binding [*xmlns-kw* xmlns-kw *userland-context* userland-context] (let [new-children (make-array 0)] - (doseq [element elements] - (if (reactive-fragment? element) - (doseq [sub-element @(:reactive-node element)] - (.push new-children sub-element)) - (.push new-children element))) + (collect-dom-nodes! new-children nodes) (-> parent-element .-replaceChildren (.apply parent-element new-children))))))))) ;; ---------------------------------------------- From b17fdacf10b012b45661b155fcbcadc12cf87d05 Mon Sep 17 00:00:00 2001 From: Vincent Cantin Date: Fri, 12 Sep 2025 08:52:27 +0300 Subject: [PATCH 2/2] Added a crash test in the test app. --- .../src/example/reactive_fragment.cljs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/example/test-app/src/example/reactive_fragment.cljs b/example/test-app/src/example/reactive_fragment.cljs index c4fe8f0..6cc2531 100644 --- a/example/test-app/src/example/reactive_fragment.cljs +++ b/example/test-app/src/example/reactive_fragment.cljs @@ -106,10 +106,32 @@ ;; Idea: write some unit tests for the for-fragment macro ,)))) +(defn dynamic-fragment-crash-test [] + ($ :article + ($ :h2 "Dynamic fragments composition (crash test)") + (vw/for-fragment* (range 2) + (fn [index1] + (vw/for-fragment* (range 2) + (fn [index2] + (let [just-started (sr/create-state true) + is-even (sr/create-state true)] + (vw/if-fragment just-started + ($ :div ($ :button {:on/click #(reset! just-started false)} "Start")) + (vw/if-fragment is-even + ($ :div + (str index1 " " index2 " ") + "The value is even." + ($ :button {:on/click #(reset! is-even false)} "Make it odd.")) + ($ :div + (str index1 " " index2 " ") + "The value is odd." + ($ :button {:on/click #(reset! is-even true)} "Make it even."))))))))))) + (defn reactive-fragment-root [] ($ :div ($ if-fragment-article) ($ case-fragment-article) ($ cond-fragment-article) ($ for-fragment-article) + ($ dynamic-fragment-crash-test) ,))