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) ,)) 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))))))))) ;; ----------------------------------------------