re-frame

2023-09-28T20:18:40.926329Z

Hello house, how would you suggest a nested db like {:id id :children [{:id id :children []...} {...} {...} ]... (with uncertain depth) be updated, assuming I have the id of a deep child? I am currently using the DB format {id1 {:id I'd children [childID1 childID1 childID1...]...} id2 {:id2 id2 :children [id3....]}.... In the second (current) approach, all the nodes are in same depth except that the parent node takes the ids of the children. So to render them, I just need to find the node from the ID and render in its parent. Which is lot easier, but rerenders all the components when any node is added or removed. The system will become slow when the nodes are large (by this approach). I just feel changing to the second approach will fix this issue. Or what's your suggestions?.. I think the first approach is the once used in re-com but component-wise

2023-11-13T08:33:54.477769Z

OK. I'll do, I just need to take care of some things first.

2023-11-13T08:33:59.116819Z

Thank you

p-himik 2023-09-29T09:00:03.148779Z

> So how do i create a simple function that will render them other than this that does that recursively? The general pattern is something like this:

(defn item [{:keys [id children-ids]}]
  (let [data @(rf/subscribe [:item-data id])]
    [:div {:key id}
     data
     (for [child-id children-ids]
       ^{:key child-id}
       [item {:id child-id}])]))
Of course, that last item can be some other component, doesn't have to be the same one. The main things here are that all the unchanging IDs are passed as arguments, all the actual data is gathered via subs only in the components that need that data (it's perfectly fine to call the same sub multiple times from multiple places - they're cached anyway), and you never call view functions as functions, you always use them as items in Hiccup vectors. > Another reason why i'm creating the IDs in the view is because, i discovered that if i create same node (plugin) multiple times (withe ID in the structure), they all tend to have the same IDs when rendered That just means you were doing something incorrectly.

2023-09-29T09:28:57.710209Z

Yes and thank you for the input. I understood this pattern actually, but why I used the "giant hiccup" one was because, i wanted to set a master view that already has all the default DOM functions in place, and should not be altered by anybody who'll create his own plugin in the future. So by this, all the plugins will follow a similar and simple pattern, instead of looping withing them for children all the time. So, the idea is that, people can be able to make their own plugin in the future, without paying attention to how the children are rendered (cos I've Done that somewhere and should not be changed by any plugin) It will be like an open source platform where other devs can just create their own plugins (only) but not touch the main code.

p-himik 2023-09-29T09:34:22.178179Z

I don't see how the approach that I outline contradicts the desire for the system to be pluggable. You can create DOM functions in the parent component and pass them down - that's fine, you don't need to create the whole tree in a single function. Behavior of particular components can easily be made customizable with multimethods, for example. And at the same time, the same pattern will still be followed by all plugins. None of the plugins will have to loop over the children.

2023-09-29T09:44:08.073519Z

OK. Lemme switch to the pattern and see how iy goes. But what about the current db map pattern, do you see any need to change it to the deep depth method I explained earlier? Currently they are all at the same level

p-himik 2023-09-29T09:49:06.294229Z

No, avoid nesting in the data. It brings nothing but problems, unless you never have duplication, never need to update anything, never need to get some deeply nested item's data without caring about its parents.

2023-09-29T09:51:07.371089Z

Thanks @p-himik, I appreciate I'll refactor and see the performance.

👍 1
p-himik 2023-09-28T20:33:09.358989Z

> I am currently using the DB format [...] That's the way to do, normalized data is much better. > Which is lot easier, but rerenders all the components when any node is added or removed. Restructure your subscriptions.

p-himik 2023-09-28T20:33:23.236479Z

Subscriptions can have other subscriptions as their input signals.

2023-09-28T20:35:38.343539Z

I use the map to render as different components recursively. It's not about subs, the logic of the recursive function and it's reliability is what I want suggestions for

2023-09-28T20:37:05.376649Z

Currently, I'm thinking to use clojure.walk but I feel it will rerender the components like the current one for every changes this time instead of node addition or subtraction alone

p-himik 2023-09-28T20:41:06.043569Z

Uhm, if you send all the data to a view, if will be re-rendered if any piece of that data changes, no matter what.

2023-09-28T20:45:36.171309Z

That's the issue. In the current setting, it only rerenders the current component only when it changes... But the systems freezes sometimes when the nodes gets heavy

p-himik 2023-09-28T20:58:09.067589Z

The desire to send all the data to the top level component (exactly what you're doing, if I understood you correctly) is not compatible with the hard reality of components re-rendering when the input data changes. You have to make a sacrifice here and pass only limited data, even to the top-level component. All the children components can derive their data from subscriptions based on the input IDs that won't be changing.

2023-09-28T21:02:44.821769Z

Currently, every node has its :key value in the map that feeds the particular component when it renders. So are you suggesting I have a separate map for those data and another nested map for the components?

2023-09-28T21:03:54.150149Z

Besides, the nodes doesn't hold the same data for all.

p-himik 2023-09-28T21:04:43.437799Z

I need a code sample to provide further comments.

2023-09-28T21:25:32.188599Z

OK. In some time. I'm on mobile phone now

2023-09-28T21:43:39.059649Z

Here's the current approach map:

{"ce-136c62fa-abde-46aa-a232-b80c936db36e"
 {:id "ce-136c62fa-abde-46aa-a232-b80c936db36e",
  :type :paragraph,
  :text "",
  :focus? false,
  :typing?
  {:nodeTree {:start [], :end []}, :position {:start 0, :end 0}}},
 "ce-082d4905-0321-4b5c-9d53-1373cfcec60a"
 {:id "ce-082d4905-0321-4b5c-9d53-1373cfcec60a",
  :type :block,
  :children ["ce-7bc1f3ce-631d-4bdf-ad48-40f9fcf66f1c"],
  :size {},
  :focus? true},
 "ce-558b764a-e83c-48b3-9277-cd4f58da991d"
 {:id "ce-558b764a-e83c-48b3-9277-cd4f58da991d",
  :type :paragraph,
  :text "",
  :focus? false},
 "ce-ea43a461-a3ad-4a7d-a06d-79a230cd324d"
 {:id "ce-ea43a461-a3ad-4a7d-a06d-79a230cd324d",
  :type :checklist,
  :text "sghghs vdgds",
  :check? true,
  :group :default,
  :replaced? true,
  :focus? false,
  :typing?
  {:nodeTree {:start [0], :end [0]}, :position {:start 12, :end 12}}},
 "ce-5b58e6d9-07f7-4faf-a09c-92708ad0e1ee"
 {:id "ce-5b58e6d9-07f7-4faf-a09c-92708ad0e1ee",
  :type :checklist,
  :text "sddskjsdjsdjkdkj",
  :check? false,
  :group :default,
  :focus? false,
  :typing?
  {:nodeTree {:start [0], :end [0]}, :position {:start 16, :end 16}}},
 "ce-beeddd2c-7714-4b56-ba5d-f322ee8484bc"
 {:id "ce-beeddd2c-7714-4b56-ba5d-f322ee8484bc",
  :type :frame,
  :orientation "horizontal",
  :children ["ce-ff46dae8-fef6-4f2f-a750-112578c965ad"],
  :style nil,
  :focus? true},
 "ce-631e1047-9202-404c-8bbe-e87bd8ff0b49"
 {:id "ce-631e1047-9202-404c-8bbe-e87bd8ff0b49",
  :type :paragraph,
  :text "nbdshsdhdshhjds",
  :focus? false,
  :typing?
  {:nodeTree {:start [0], :end [0]}, :position {:start 15, :end 15}}},
 "ce-b074774a-0cb7-48f7-b30d-07fb619977c9"
 {:id "ce-b074774a-0cb7-48f7-b30d-07fb619977c9",
  :type :frame,
  :orientation "horizontal",
  :children ["ce-90e3ab4a-49e4-4ec9-8048-b0f6a12bf2f5"],
  :style nil,
  :focus? true},
 "ce-ff46dae8-fef6-4f2f-a750-112578c965ad"
 {:id "ce-ff46dae8-fef6-4f2f-a750-112578c965ad",
  :type :block,
  :children ["ce-0f2538d7-fb02-4465-ae70-297d8f7ba76e"],
  :size {},
  :focus? true},
 "ce-391d8be6-bef4-45cc-b23c-38255398584b"
 {:id "ce-391d8be6-bef4-45cc-b23c-38255398584b",
  :type :frame,
  :orientation "vertical",
  :children
  ["ce-0561f7f1-7ea4-4b9d-9773-560b1f302b6a"
   "ce-082d4905-0321-4b5c-9d53-1373cfcec60a"
   "ce-5508fc1f-5c4b-4fb0-848a-205fb58458b7"],
  :style nil,
  :focus? true},
 "ce-0f2538d7-fb02-4465-ae70-297d8f7ba76e"
 {:id "ce-0f2538d7-fb02-4465-ae70-297d8f7ba76e",
  :type :paragraph,
  :text "hello world<br><br>",
  :focus? true,
  :typing?
  {:nodeTree {:start [0], :end [0]}, :position {:start 11, :end 11}}},
 "ce-943f54db-6d18-41a3-8f81-0eacbf7dbe69"
 {:id "ce-943f54db-6d18-41a3-8f81-0eacbf7dbe69",
  :type :paragraph,
  :text "",
  :focus? true,
  :typing?
  {:nodeTree {:start [], :end []}, :position {:start 0, :end 0}}},
 "ce-9a77fe09-9df9-49fe-99b1-c1817c74da15"
 {:id "ce-9a77fe09-9df9-49fe-99b1-c1817c74da15",
  :type :paragraph,
  :text "<br>",
  :focus? true,
  :typing?
  {:nodeTree {:start [], :end []}, :position {:start 0, :end 0}}},
 "ce-7d559404-fdd2-48cd-b02f-b1002ddee87b"
 {:id "ce-7d559404-fdd2-48cd-b02f-b1002ddee87b",
  :type :frame,
  :orientation "vertical",
  :children
  ["ce-0561f7f1-7ea4-4b9d-9773-560b1f302b6a"
   "ce-e8429709-2dcb-48d5-9c8d-d536c98597a8"
   "ce-082d4905-0321-4b5c-9d53-1373cfcec60a"
   "ce-5508fc1f-5c4b-4fb0-848a-205fb58458b7"],
  :style nil,
  :focus? true},
 "ce-5508fc1f-5c4b-4fb0-848a-205fb58458b7"
 {:id "ce-5508fc1f-5c4b-4fb0-848a-205fb58458b7",
  :type :block,
  :children ["ce-5b58e6d9-07f7-4faf-a09c-92708ad0e1ee"],
  :size {},
  :focus? true},
 "ce-0561f7f1-7ea4-4b9d-9773-560b1f302b6a"
 {:id "ce-0561f7f1-7ea4-4b9d-9773-560b1f302b6a",
  :type :block,
  :children ["ce-ea43a461-a3ad-4a7d-a06d-79a230cd324d"],
  :size {},
  :focus? true},
 "ce-e7990dba-448b-4430-9e26-8a9960897cb2"
 {:id "ce-e7990dba-448b-4430-9e26-8a9960897cb2",
  :type :paragraph,
  :text "dsghhdhgdsnms<br>",
  :focus? false,
  :typing?
  {:nodeTree {:start [0], :end [0]}, :position {:start 13, :end 13}}},
 "ce-a186684c-5f62-4d4a-a33d-e1b7ecb94c23"
 {:id "ce-a186684c-5f62-4d4a-a33d-e1b7ecb94c23",
  :type :block,
  :children ["ce-e7990dba-448b-4430-9e26-8a9960897cb2"],
  :size {},
  :focus? true},
 "title" {:focus? false},
 "ce-7bc1f3ce-631d-4bdf-ad48-40f9fcf66f1c"
 {:id "ce-7bc1f3ce-631d-4bdf-ad48-40f9fcf66f1c",
  :type :checklist,
  :text "smnjjsdjkjks",
  :check? false,
  :group :default,
  :focus? false,
  :typing?
  {:nodeTree {:start [0], :end [0]}, :position {:start 12, :end 12}}},
 "ce-839f14f7-85a8-4c48-a9ff-3ace54634096"
 {:id "ce-839f14f7-85a8-4c48-a9ff-3ace54634096",
  :type :paragraph,
  :text "",
  :focus? true,
  :typing?
  {:nodeTree {:start [], :end []}, :position {:start 0, :end 0}}},
 "ce-f715000a-75e3-409f-96cf-f9fe7edc6e5a"
 {:id "ce-f715000a-75e3-409f-96cf-f9fe7edc6e5a",
  :type :frame,
  :orientation "horizontal",
  :children
  ["ce-7d559404-fdd2-48cd-b02f-b1002ddee87b"
   "ce-391d8be6-bef4-45cc-b23c-38255398584b"],
  :style nil,
  :focus? true},
 "ce-e8429709-2dcb-48d5-9c8d-d536c98597a8"
 {:id "ce-e8429709-2dcb-48d5-9c8d-d536c98597a8",
  :type :block,
  :children ["ce-631e1047-9202-404c-8bbe-e87bd8ff0b49"],
  :size {},
  :focus? true},
 "Untitled-d73e9656-8a91-4acb-a626-659c1a1117bf" {:title ""},
 "ce-acc62480-5102-41f0-92e8-84c08030685c"
 {:id "ce-acc62480-5102-41f0-92e8-84c08030685c",
  :type :paragraph, 
  :text "", 
  :focus? true}}

2023-09-28T21:45:20.571769Z

all the nodes are on the same level, but the parents have their children IDs as a vector. So by this, i can search for the node map, and render them (recursively).

2023-09-28T21:50:37.173049Z

So, by this, if any node is added, it re-renders all of them instead of just the node that will parent it. So i'm thinking of moving the approach to putting every children map into a vector of the key :children contained in the parent node, so if i add a new node, the to-be parent will be the only node to be re-rendered. Now, the logic to update a certain node in an uncertain depth without triggering a whole db update is the problem now

p-himik 2023-09-28T21:55:59.008649Z

I'm more interested in an approximate structure of your views and relevant subscriptions.

2023-09-28T22:00:00.258029Z

This is an event that updates a node

(reg-event-db
 :nodes/update
 (undoable "A node was updated") 
 (fn [db [_ page-id node-id field & value]]
   (if value
     (assoc-in db [:pages page-id :nodes node-id field] (first value))
     (update-in db [:pages page-id :nodes node-id] conj field))))

p-himik 2023-09-28T22:02:13.492539Z

*event, not a subscription

2023-09-28T22:02:42.523559Z

The render code here:

2023-09-28T22:04:37.466929Z

you can preview the db here: https://www.flatter.to/editor

p-himik 2023-09-28T22:09:27.412679Z

Sorry but your code is as far from what it's supposed to be in the realm of re-frame/Reagent as possible. • Don't pass all the data around (hard to see what's going on exactly on the video, but seems like that's the case), use very specific subscriptions instead • Don't call functions everywhere, turn most of them into Reagent components • Don't use random :key values • Don't generate random data on render There may be other issues, I haven't looked past these. There's a lot of work that code needs before it'd become something I'd approve if it were in some PR that I had to review.

2023-09-28T22:27:04.152959Z

The functions are utility functions that are used many times in the whole project, also, they don't return anything other than map except for this:

(defn render-elements [node & child]
  (let [click-fn (fn []
                   (.trigger (js/$ "#settings-btn") "click"))
        mouse-on-fn (set-active-node)
        mouse-out-fn (remove-active-node)
        db-click-fn (db-click-fn)
        drag-start-fn (drag-start-fn)
        drag-over-fn (drag-over-fn)
        drop-fn (drag-end-fn)
        enter-fn (ENTER-fn)
        key-down-fn (key-down-fn)
        paste-fn (paste-fn)
        toggle-click-fn (handle-click-fn)
       ;;  select-fn (selection-fn)
        ;; node (if (= @(subscribe [:nodes/focus]) (:id node)) (assoc node :focus? true) (assoc node :focus? false))
        node (assoc node
                    :click-fn click-fn
                    :mouse-on-fn mouse-on-fn
                    :mouse-out-fn mouse-out-fn
                    :db-click-fn db-click-fn
                    ;; :drop-fn drop-fn
                    :drag-over-fn drag-over-fn
                    :enter-fn enter-fn
                    :key-down-fn key-down-fn
                    :paste-fn paste-fn
                    :toggle-click-fn toggle-click-fn
              ;;       :select-fn select-fn
                    )
        render-fn (fn [ele node]
                    (let [id (:id node)
                          parent-id (get-node-parent-id id)
                          has-parent? (if (or parent-id (seq? parent-id)) true false)
                          block_id (str "ce-" (random-uuid))
                          content_id (str "ce-" (random-uuid))]
                      (if-not has-parent?
                        [:div {:key (rand 9876543567)
                               :id block_id
                               :class "ce-block"
                               :on-mouse-over (remove-active-node)}
                         [:div {:key (rand-int 98765435678)
                                :id content_id
                                :class "ce-block__content"} ele]]
                        ele)))
        element (render-options node child)]
    (render-fn element node)))
The (let []) bindings are functions for the DOM, passed as props into each plugin (node). This:
[:div {:key (rand 9876543567)
                               :id block_id
                               :class "ce-block"
                               :on-mouse-over (remove-active-node)}
                         [:div {:key (rand-int 98765435678)
                                :id content_id
                                :class "ce-block__content"} ele]]
is generated for every new block, but doesn't go to the DB, its just for proper styling only. I have nodes that are parents, and those that are children as shown in the db map above. I do render them as Reagent components (all of them). This is the simplest of the plugins (components) i am referring to:
(def menu
  {:name "Frame"
   :type :frame
   :icon icons/IconTableWithoutHeadings
   :class ""})

(def structure
  {:id (str "ce-" (random-uuid))
   :type :frame
   :orientation "horizontal" ;; or horizontal
   :children []
   :style nil})

(defn element [node & children]
  (let [{id :id
         orientation :orientation
         db-click-fn :db-click-fn
         style :style} node]
    [:div {:key (rand-int 324567890)
           :id id
           :style (or style {:flex 1 :min-width "20%" :max-width "100%"})
           :class (str "frame cdx-block__frame__" orientation)
           :on-click  db-click-fn}
     children]))
only [element] is rendered , structure goes to the db as a map

2023-09-28T22:28:52.100289Z

Every plugins have different structure and element Also, the events and subs, are fundamental to all the plugins except for plugins that requires specific event and subscription. Ids must be randomly and uniquely generated cos, when you drop a plugin after D'n'D, it copies /moves the plugin to a new parent, which requires a new unique ID, i.e a checkbox will check its identical node when clicked cos they have same ID

p-himik 2023-09-28T23:02:02.467899Z

I didn't mean utility functions, I meant functions that return Hiccup. Your code ends up with a single giant Hiccup structure. Instead, that structure should be small. All the inner components will generate their own structure. In other words, in many places, (f) should actually be [f].

p-himik 2023-09-28T23:02:45.253699Z

> Ids must be randomly and uniquely generated That's fine to have random IDs. But you should not generate them in views - instead, generate them in whatever ends there are that populate the data.

2023-09-28T23:19:01.446899Z

Ok. So how do i create a simple function that will render them other than this that does that recursively? It is a

parent -> children -> grand-children ->>> 
DOM, and the map is a single level map. Hw do i render them in small small pieces that will not be as this (recursive)? Would you suggest a simpler pattern or code for this?

2023-09-28T23:24:27.671229Z

Another reason why i'm creating the IDs in the view is because, i discovered that if i create same node (plugin) multiple times (withe ID in the structure), they all tend to have the same IDs when rendered

2023-09-28T23:29:26.522849Z

The map method i am using, is same as used by Figma i guess, cos i've coded a page that renders Figma JSON into HTML & CSS in the past. Thats where the Idea of same level nodes came from. Now, I'm thinking of switching to the Webflow JSON pattern (which i've also worked with before) of having nodes in their depth and hierarchy (parent -> children) and not same level

2023-11-11T16:27:17.390529Z

Hello @p-himik, I've implemented the children loops using the (for...) loop (i.e every component loop through to render their own children instead of the one big recursive component as before), but the issue now is, as the loop goes like 4 children deep, it becomes infinite and freezes the system. I don't really know what's going on. It actually works fine when the loop is <4 children deep.

p-himik 2023-11-11T16:33:32.541139Z

> I don't really know what's going on. Well, neither do I - I'm not an oracle, unfortunately.

2023-11-11T16:37:37.826479Z

😂 Actually...  I'll try to figure it, then I'll post the code if I had to. Thanks

2023-11-11T17:03:48.108149Z

Please do you have any idea how I can memoize a component? I can't find reagent.core/memo or if you can suggest a better way to do it. Here's my code:

(defn render-children [children funcs]
  (when-not (empty? children)
    (doall (for [child-id children
                 index (range (count children))]
             (let [child (search-node child-id)]
               [render-options child funcs])))))


(defn render-options [node functions]
  (let [{type :type} node]
    (case type
      :frame [frame/element node functions]
      :block [block/element node functions]
      :paragraph [paragraph/element node functions]
      :header [headers/element node functions]
      :list [list/element node functions]
      :image [image/element node functions]
      :checklist [checklist/element node functions]
      :toggle-list [toggle-list/element node functions]
      :delimiter [delimiter/element node]
      :link [link/element node functions]
      :page [page/element node functions]
      :quote [quote/element node functions]
      nil)))
list/element
(defn element [node funcs]
  (let [{id :id
         text :text
         tag :tag
         children :children} node
         {key-down-fn :key-down-fn
          set-active-fn :mouse-on-fn
          remove-active-fn :mouse-out-fn
          render-children :render-children
          list? :list?} funcs
        class (if (= tag :ol) 
                " cdx-nested-list--ordered" 
                " cdx-nested-list--unordered")
        list-type (if (= tag :ol)
                    {:list-style "number"}
                    {:list-style "disc"})
        parent-id (atom nil)]
    
       (r/create-class {:component-did-mount (fn [comp]
                                              ;;  (.addEventListener (.querySelector (rdom/dom-node comp) ".tt") "selectstart" (fn []
                                              ;;                                                                                 (.addEventListener js/document "selectionchange" (selection-start-fn))))
                                               (.addEventListener (rdom/dom-node comp) "focusin" (fn [evt]
                                                                                                   (set-active-fn evt)
                                                                                                   (dispatch [:nodes/set-focus id])))
                                               (let [_this (rdom/dom-node comp)
                                                     ancestor _this.parentNode.parentNode]
                                                 (reset! parent-id ancestor.id)))
                        :reagent-render (fn []
                                          
                                            (if (and list? (empty? children))
                                              [:span.list
                                               {:id id
                                                :key id
                                                :class (str "cdx-nested-list cdx-block" class)
                                                :contentEditable true
                                                :style (merge {:border-right "30px" :width "100%"})
                                                :on-key-down key-down-fn
                                                :data-parent @parent-id
                                                :data-type "List"
                                                :data-placeholder "Press 'TAB' or '/' for Menus..."
                                                :suppressContentEditableWarning true
                                                :dangerouslySetInnerHTML {:__html text}}]

                                              [:div.list.frame.cdx-block__frame__vertical
                                               {:key id
                                                :id id}
                                               [:span.list
                                                {:id id
                                                 :class (str "cdx-nested-list cdx-block" class)
                                                 :contentEditable true
                                                 :style (merge {:border-right "30px" :width "100%"})
                                                 :on-key-down key-down-fn
                                                 :data-parent @parent-id
                                                 :data-type "List"
                                                 :data-placeholder "Press 'TAB' or '/' for Menus..."
                                                 :suppressContentEditableWarning true
                                                 :dangerouslySetInnerHTML {:__html text}}]
                                               (render-children children (assoc funcs :list? true))]))})))
children is a vector of ids

p-himik 2023-11-11T17:19:25.152199Z

> Please do you have any idea how I can memoize a component? Why would you want to do that in the first place? Some general comments on your code: • That for at the top results in N^2 items. You don't use index there so I don't know whether it's intended or not • Don't use rdom/dom-node - it has been deprecated. Instead, use React refs • The list/element component will not pass a proper parent-id after the first render. I don't know why you need the :data-parent attribute at all, but if you're in control of the code that consumes that attribute then I'd suggest restructuring the code in such a way so that children components don't need a direct reference to one of their parents • Use :keys destructuring to avoid name repetition, e.g. (let [{:keys [id text tag children]} node] ...) • No need to concatenate strings yourself when composing classes for Reagent components - use reagent.core/class-names instead, it will filter out nils and expand collections for you, and classes can be keywords

2023-11-11T18:11:59.301349Z

• The :data-parent and the parent-id isn't useful, i tried using it to test something (Which node is it's parent in the map, not the in the DOM) but i saw no need to use it, i just left it there as i was trying to show the code. • For the class, noted. But the class isn't useful here too, i just used it in a previous structure, so i won't be needing it in this (will probably remove it) • So how do i deal with the for ? (I think this is the main issue) The index, i intended using it as a :key for the loop though

p-himik 2023-11-11T18:16:50.661469Z

I don't see anything in your code that would cause an infinite loop. If you create a publicly available minimal reproducible example, I can take a look.

2023-11-11T18:53:31.695879Z

(defn render-children [children funcs]
  (when-not (empty? children)
    (doall (for [child-id children
                 index (range (count children))]
             (let [child (search-node child-id)]
              ^{:key index}
               (render-options child funcs))))))

(defn render-options [node functions]
  (let [{type :type} node]
    (case type
      :frame [frame/element node functions]
      :block [block/element node functions]
      :paragraph [paragraph/element node functions]
      :header [headers/element node functions]
      :list [list/element node functions]
      :image [image/element node functions]
      :checklist [checklist/element node functions]
      :toggle-list [toggle-list/element node functions]
      :delimiter [delimiter/element node]
      :link [link/element node functions]
      :page [page/element node functions]
      :quote [quote/element node functions]
      nil)))


(defn render []
  (let [blocks (fetch [:nodes/get-blocks])
        blocks-node (map #(search-node %) blocks)]
    (if (not blocks)
      (put [:initialize-db!])
      (doall  (for [block blocks-node
                    :let [{id :id} block
                          _id (str "ce-" (random-uuid))
                          click-fn (fn []
                                     (.trigger (js/$ "#settings-btn") "click"))
                          funcs {:click-fn click-fn
                                 :mouse-on-fn (set-active-node)
                                 :mouse-out-fn (remove-active-node)
                                 :db-click-fn (db-click-fn)
                                  ;; :drop-fn drop-fn
                                 :drag-over-fn (drag-over-fn)
                                 :key-down-fn (key-down-fn)
                                 :key-up-fn (key-up-fn)
                                 :paste-fn (paste-fn)
                                 :toggle-click-fn (handle-click-fn)
                                 :render-children render-children
                                  ;; :select-fn select-fn
                                 :add-block add-block
                                 :add-node add-node}]]
                [:div {:key id
                       :id id
                       :class "ce-block"}
                 [:div {:key (str id (rand-int 98765435678))
                        :id _id
                        :on-mouse-leave remove-active-node
                        :on-mouse-out remove-active-node
                        :class "ce-block__content"} (render-options block funcs)]])))))
list/element
(defn element [node funcs]
  (let [{id :id
         text :text
         tag :tag
         children :children} node
        
         {key-down-fn :key-down-fn
          set-active-fn :mouse-on-fn
          remove-active-fn :mouse-out-fn
          render-children :render-children
          list? :list?} funcs
        class (if (= tag :ol) 
                " cdx-nested-list--ordered" 
                " cdx-nested-list--unordered")
        list-type (if (= tag :ol)
                    {:list-style "number"}
                    {:list-style "disc"})]
    
       (r/create-class {:component-did-mount (fn [comp]
                                              ;;  (.addEventListener (.querySelector (rdom/dom-node comp) ".tt") "selectstart" (fn []
                                              ;;                                                                                 (.addEventListener js/document "selectionchange" (selection-start-fn))))
                                               (.addEventListener (rdom/dom-node comp) "focusin" (fn [evt]
                                                                                                   (set-active-fn evt)
                                                                                                   (dispatch [:nodes/set-focus id]))))
                                        
                        :reagent-render (fn []
                                          
                                            (if (and list? (empty? children))
                                              [:span.list
                                               {:id id
                                                :key id
                                                :class (str "cdx-nested-list cdx-block" class)
                                                :contentEditable true
                                                :style (merge {:border-right "30px" :width "100%"})
                                                :on-key-down key-down-fn
                                                :data-type "List"
                                                :data-placeholder "Press 'TAB' or '/' for Menus..."
                                                :suppressContentEditableWarning true
                                                :dangerouslySetInnerHTML {:__html text}}]

                                              [:div.list.frame.cdx-block__frame__vertical
                                               {:key id
                                                :id id}
                                               [:span.list
                                                {:id id
                                                 :class (str "cdx-nested-list cdx-block" class)
                                                 :contentEditable true
                                                 :style (merge {:border-right "30px" :width "100%"})
                                                 :on-key-down key-down-fn
                                                 :data-type "List"
                                                 :data-placeholder "Press 'TAB' or '/' for Menus..."
                                                 :suppressContentEditableWarning true
                                                 :dangerouslySetInnerHTML {:__html text}}]
                                                 (render-children children (assoc funcs :list? true))]))})))
A typical (minimal) DB
{:nodes
 {"ce-bbeb5451-ec21-45df-bfc6-33ee6062cc25"
  {:id "ce-bbeb5451-ec21-45df-bfc6-33ee6062cc25",
   :type :frame,
   :orientation "horizontal",
   :children ["ce-811a09ef-1ca8-4b20-bace-cb65d62a528a"],
   :style nil,
   :focus? true},
  "ce-811a09ef-1ca8-4b20-bace-cb65d62a528a"
  {:id "ce-811a09ef-1ca8-4b20-bace-cb65d62a528a",
   :type :block,
   :children ["ce-0b482160-d0b7-402a-8337-c38e1bde48ef"],
   :size {},
   :level 0,
   :focus? true},
  "ce-906c045f-d591-4270-a2cc-a051a85baa7c"
  {:id "ce-906c045f-d591-4270-a2cc-a051a85baa7c",
   :type :paragraph,
   :text "sdjksdkjdsk",
   :typing?
   {:nodeTree {:start [0], :end [0]}, :position {:start 11, :end 11}}},
  "ce-0b482160-d0b7-402a-8337-c38e1bde48ef"
  {:id "ce-0b482160-d0b7-402a-8337-c38e1bde48ef",
   :type :list,
   :tag :ol,
   :text "",
   :size {},
   :focus? true,
   :children ("ce-92391c6c-5057-4f25-940a-d0b5d55f246b")},
  "ce-f495277e-ac8d-4b6a-af28-0b56a7b7442a"
  {:id "ce-f495277e-ac8d-4b6a-af28-0b56a7b7442a",
   :type :block,
   :children ["ce-906c045f-d591-4270-a2cc-a051a85baa7c"],
   :size {}},
  "ce-92391c6c-5057-4f25-940a-d0b5d55f246b"
  {:id "ce-92391c6c-5057-4f25-940a-d0b5d55f246b",
   :type :block,
   :children ["ce-59dc71af-1652-43a2-afa1-4995255b454f"],
   :size {},
   :level 0,
   :focus? true},
  "ce-59dc71af-1652-43a2-afa1-4995255b454f"
  {:id "ce-59dc71af-1652-43a2-afa1-4995255b454f",
   :type :list,
   :tag :ol,
   :text "",
   :size {},
   :focus? true,
   :typing?
   {:nodeTree {:start [0], :end [0]}, :position {:start 0, :end 0}}},
  "ce-9a4d47bc-74fa-4109-9131-317d544bad0b"
  {:id "ce-9a4d47bc-74fa-4109-9131-317d544bad0b",
   :type :frame,
   :orientation "horizontal",
   :children ["ce-f495277e-ac8d-4b6a-af28-0b56a7b7442a"],
   :style nil}},
 :blocks
 ["ce-9a4d47bc-74fa-4109-9131-317d544bad0b" "ce-bbeb5451-ec21-45df-bfc6-33ee6062cc25"],
 :focus "ce-59dc71af-1652-43a2-afa1-4995255b454f", 
}
This is the code the renders the component in the editor. The components get their data (props) from the db based on their :type Don't know if this will help

p-himik 2023-11-11T18:54:46.831849Z

That's not a reproducible example, it's just a bunch of code blocks that I'd have to somehow string together in order to make them work. Can you create a public repo with all the right files and instructions on how to reproduce the error?