Fork me on GitHub
#hyperfiddle
<
2023-10-19
>
Vincent02:10:17

I'm recursively rendering reply/comment components and getting StackOverflows, I wonder if there is a way to do it without poppin the stack. Code in thread

Vincent02:10:34

(e/defn ItemView []
  (e/server
    (let [e (xt/entity db current-item-xt-id)
          xt-id (:xt/id e)
          item-xt-id xt-id
          link (:item/link e)
          item-id (:item/id e)
          author (:item/minted-by e)
          minted-at (:item/minted-at e)]
      (e/client
        (dom/div (dom/props {:class "itemview fc"})
          (dom/div (dom/props {:class "fi"})
            (dom/text link))
          (dom/div (dom/props {:class "fi"})
            (dom/text author))
          (dom/div (dom/props {:class "fi"})
            (dom/text item-id))
          (dom/div (dom/props {:class "fi"})
            (dom/text minted-at))
        (when (not= current-item-xt-id "")
          (dom/div (dom/props {:class "reply-input fi"})
            (ReplyCreate. xt-id xt-id)))
        (dom/div (dom/props {:class "replies fr"})
          (e/server (e/for-by :xt/id [{:keys [xt/id]} (e/offload #(reply-with-descendant-records db item-xt-id item-xt-id))] (ItemNestedReplies. id)))))))))

(e/defn ItemNestedReplies [xt-id]
  (e/server
    (let [;i (xt/entity db current-item-xt-id)
          r (xt/entity db xt-id)
          text (:reply/text r)
          author (:reply/minted-by r)
          minted-at (:reply/minted-at r)
          item-xt-id (:reply/item-xt-id r)
          upvotes (:reply/upvotes r)
          parent (:reply/parent-xt-id r)]
      (e/client
        (dom/div (dom/props {:class "itemreplies fc"})
         (dom/div (dom/props {:class "fc"})
          (dom/div (dom/props {:class "fi"})
            (dom/text text))
          (dom/div (dom/props {:class "fi"})
            (dom/text "xt-id: " xt-id))
          (dom/div (dom/props {:class "fi"})
            (dom/text "item: " item-xt-id))
          (dom/div (dom/props {:class "fi"})
            (dom/text "parent: " parent))
          (dom/div (dom/props {:class "fi"})
            (dom/text author))
          (dom/div (dom/props {:class "fi"})
            (dom/text upvotes))
          (dom/div (dom/props {:class "fi"})
            (dom/text minted-at))
          (dom/div (dom/props {:class "fi"})
            (NestedReplyCreate. current-item-xt-id xt-id))
           (dom/div (dom/props {:class "fi"})
             (dom/div (dom/props {:class "replies fc"})
               (e/server (e/for-by :xt/id [{:keys [xt/id]} (e/offload #(reply-with-descendant-records db item-xt-id xt-id))] (e/client (dom/div (dom/text id))))))) ;(ItemNestedReplies. id)))))
          (dom/div (dom/props {:class "fi"})
             (ui/button (e/fn [] (e/server (e/discard (xt/submit-tx !xtdb [[:xtdb.api/delete xt-id]])))) (dom/text "✗")))))))))

;;item == newsitem
;;parent == parent
(e/defn ReplyCreate [item-xt-id parent-xt-id] (e/client (InputSubmit. "reply"  (e/fn [v] (e/server (e/discard (e/offload #(xt/submit-tx !xtdb 
  [[:xtdb.api/put
    {:xt/id (random-uuid)
    :reply/text v
    :reply/id (nid)
    :reply/minted-by online-user
    :reply/upvotes 0
    :reply/item-xt-id item-xt-id
    :reply/parent-xt-id parent-xt-id
    :reply/minted-at (System/currentTimeMillis)}]]))))))))

(e/defn NestedReplyCreate [item-xt-id parent-xt-id] (e/client (when (and item-xt-id parent-xt-id) (InputSubmit. "nested reply"  (e/fn [v] (e/server (e/discard (e/offload #(xt/submit-tx !xtdb 
  [[:xtdb.api/put
    {:xt/id (random-uuid)
    :reply/text v
    :reply/id (nid)
    :reply/minted-by online-user
    :reply/upvotes 0
    :reply/item-xt-id item-xt-id
    :reply/parent-xt-id parent-xt-id
    :reply/minted-at (System/currentTimeMillis)}]])))))))))

#?(:clj
   (defn reply-with-descendant-records [db item-xt-id parent-xt-id]
     (->> (xt/q db '{:find [(pull ?r [:xt/id :reply/text :reply/item-xt-id :reply/parent-xt-id :reply/upvotes])]
                     :where [[?r :reply/item-xt-id item-xt-id]
                             [?r :reply/parent-xt-id parent-xt-id]]
                     :in [item-xt-id parent-xt-id]} item-xt-id parent-xt-id)
       (map first)
       vec)))

Vincent02:10:32

You can see the commented out end of the line (e/server (e/for-by :xt/id [{:keys [xt/id]} (e/offload #(reply-with-descendant-records db item-xt-id xt-id))] (e/client (dom/div (dom/text id))))))) ;(ItemNestedReplies. id))))) And the reason this comment is there is because if I delete from (e/client...) and uncomment that, I get a stackOverflow. I think i need a way to "bottom out" or "zero out" on the recursive query :thinking_face:

Vincent03:10:58

I added another component that does not recurse / recur and it "bottoms out" on 1 nested comment. I could artificially mint several more to add a semblance of depth / nesting. I thought I could do the same with a depth and max-depth

xificurC06:10:59

Please post code snippets longer than 3 lines as a slack snippet (Ctrl+Shift+Enter or start typing /snippet) or a a github gist or repo

xificurC06:10:06

Also please include the exception and where you saw it (clj console, browser etc)

xificurC06:10:04

we've landed fixes on code size and nesting on the main branch, could you retry with the latest git sha?

xificurC06:10:33

did you check you're not infinitely recursing?

Dustin Getz10:10:14

how deep is the recursion? (in addition to what Peter asked)

Vincent14:10:14

I think it should stop becuase the database result is empty :thinking_face:

Dustin Getz14:10:19

you need a base case, an if statement guarding the recur point

Dustin Getz14:10:23

def factorial(n):
    if n > 0:
        return n * factorial(n - 1)
    else: # base case, no recur
        return 1

Vincent14:10:50

Okay that makes sense. I attempted "let the replies be (e/offload...) if replies render otherwise no render, but that also not working ...

xificurC14:10:16

I think (e/for-by [] ...) will be the base case here, when the query returns an empty resultset

👍 1
1
😮 1
Dustin Getz14:10:29

oh i must have misread

xificurC14:10:35

you can println to make sure you're not recursing indefinitely

Vincent14:10:58

So in theory, i can check if replies has any entries, and render or not. I think I understand the concept but I am still running into StackOverflows. If I print replies to console I see [] or [...] results ... I must be doing something very naively

xificurC14:10:39

please include the exception you're seeing here in the chat. Have you tried latest electric sha?

Vincent15:10:07

let me bump up the version ty for reminding 😅

Vincent15:10:13

😅 some stuff broke but unclear why ... investigating. some of my components stopped rendering

Vincent15:10:10

Hmm.. I had to roll back

Vincent15:10:36

My online list of users, the chat, and some real-time components nested in there were not rendering at all with the bump so I rolled back to v2-alpha-349-ge9996713

Vincent15:10:31

you are right that the query returns an empty result [] ... not sure how i can prevent rendering once I know that, I think I am missing something fundamental about execution sequence / assignment sequence

xificurC16:10:34

Rendering will stop at that point. Just like (mapv foo []) never runs foo

Vincent17:10:05

Okay, I expected that to be true, but I encounter stack overflows, so :man-shrugging: tap out for now lol 4 levels deep via manual component creation (aka 4 new components) is fine for now and the forseeable future. if i can figure out "infinite replies" on comments that would be awesome, but it overflows so, gotta focus on tubs i can manage the water level in 😅

nivekuil18:10:16

was trying the ic branch for fun and though incremental is still broken, it seems to be a lot faster (13s->8.5s) with no other changes

xificurC19:10:30

were you trying to start an application? There's no entrypoint right now

nivekuil19:10:40

oh, I thought boot-client would have been the entrypoint. but just the same old boot seems much faster too

xificurC19:10:02

today the client boots and sends electric IR over to the server in the first websocket message. The server interprets the IR. With IC we split the code and the both peers run their compiled part of the program

xificurC19:10:51

the speedup you saw might just be the fact only half of the code is built

nivekuil19:10:11

i'm not using any of the -ic namespaces

xificurC19:10:44

then I don't know 🙂

nivekuil19:10:12

maybe some shared code you optimized? anyways thanks 🙂

macrobartfast23:10:50

Does anyone have a workflow they can share for reusable UI components (like the React crowd often leans toward)? And as a side question, and less importantly, can anyone share any inspiring app examples, UI'wise? I understand the magic of Electric is not focused on the UI, but the graphical interface is a practicality I have to deal with, alas.

Vincent00:10:51

Reusable ui components? I think all components should be reusable if possible, a component for each view is optional, and a component for each item you're going to iterate over... My workflow is basically: 1. get a working example repo and merge/mold them together if I need 2 features 2. create InputSubmits. for each of the data thingies I need, like posts, or users, or whatever 3. Generate some data via text input and have them show to the ui immediately 4. basically make sure i have this immediate database-like view on all the data before i start drawing up more comlpex components 5. then, start working on the app in terms of interface, 6. in electric land, lines of code currently affets compilation time so... fewer components is better.! 😅

macrobartfast00:10:34

Thanks tremendously for this!

😊 1