0

I have a ClojureScript component:

(defn main-panel []

  (def nodes (-> @(re-frame/subscribe [::subs/nodes])))

  (defn list-nodes [node]
    (prn (node :name)))

  (reagent/create-class
   {:component-did-mount
    (fn []
      (api-service/get-file-tree))

    :reagent-render
    (fn []
      (doseq [node nodes]
        (if (= (node :depth) 0)
          (list-nodes node))))}))

which prints strings to the console.

But when I change the first function to:

  (defn list-nodes [node]
    [:<>
     [:h1 (node :name)]])

I don't get any HTML that is rendered - no errors and the browser window stays blank.

How can I change this so it renders html?

Update

In response to the answer below I have changed doseq for for. The issue is now that I am getting the error Uncaught Invariant Violation: Objects are not valid as a React child which is fine if you're working with React because you can debug, but with reagent, not so much. consider the following. This function:

(defn node [nodes]
  (prn (nodes :name)))

when called by this function:

    :reagent-render
    (fn []
      (for [nodes all-nodes]
        (if (= (nodes :depth) 0)
          (do
            (nodeComponent/node nodes)
            ))))

prints 5 strings. But changing the called function to this:

(defn node [nodes]
  [:h1 (nodes :name)])

causes this error: Uncaught Invariant Violation: Objects are not valid as a React child.

Is this problem somehow related to another problem I'm having?

1 Answer 1

2

For one, def and defn create global bindings. In order to create local bindings, use let or letfn:

(defn main-panel []
  (let [nodes (-> @(re-frame/subscribe [::subs/nodes]))
    (letfn [(list-nodes [node]
              (prn (node :name)))]
      (reagent/create-class
       {:component-did-mount
        (fn []
          (api-service/get-file-tree))
        :reagent-render
        (fn []
          (doseq [node nodes]
            (if (= (node :depth) 0)
              (list-nodes node))))}))

Your problem is that the render function should return the hiccup forms, but doseq returns nil. The function inside gets run (that's why you see console output from prn), but its return value is thrown away (that's why you see no HTML). An immediate fix might be to replace doseq with for, but

You could maybe do it like this (first reagent form):

(defn main-panel []
  [:ul (for [node @(re-frame/subscribe [::subs/nodes])
             :when (zero? (:depth node))]
         ^{:key (:id node)} ; whatever identifies nodes, for performance
         [:li {:class "node"} (:name node)]])

I would trigger data fetching not here but wherever you startup your application (more or less parallel to the initial app render step).

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks @Svante - but why would that make a difference and how do you propose I use let or letfn in this instance to make it work? :<> is just hiccup for a React fragment and should not make a difference used in this context.
Thanks for the updated answer @Svante - I don't want to return a list or unordered list, I'm just trying to render a function containing hiccup for every node I pass it - I've tried changing the the doseq to a for, but I get Uncaught Invariant Violation: Objects are not valid as a React.
Again, you need to actually return the hiccup forms from your render function.
Thanks, but I just can't get it to work. I think I am returning hiccup, but I'm not and I don't know why. nodeComponent/node nodes calls function node which has only hiccup in it. I've even tried putting hiccup straight in to :reagent-render as the last form in the function.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.