reagent

A minimalistic ClojureScript interface to React.js http://reagent-project.github.io/
DFST 2021-06-15T05:05:23.109500Z

I'm having trouble working with the primereact ui library, specifically this (trimmed for space) templating example from https://primefaces.org/primereact/showcase/#/datatable/templating:

export class DataTableTemplatingDemo extends Component {

    constructor(props) {
        super(props);

        this.state = {
            products: []
        };
        this.ratingBodyTemplate = this.ratingBodyTemplate.bind(this);
        this.statusBodyTemplate = this.statusBodyTemplate.bind(this);
    }

    ratingBodyTemplate(rowData) {
        return <Rating value={rowData.rating} readOnly cancel={false} />;
    }

    statusBodyTemplate(rowData) {
        return <span className={`product-badge status-${rowData.inventoryStatus.toLowerCase()}`}>{rowData.inventoryStatus}</span>;
    }

    render() {
        
        return (
            <div className="datatable-templating-demo">
                <div className="card">
                    <DataTable value={this.state.products}>
                        <Column field="name" header="Name"></Column>
                        <Column field="rating" header="Reviews" body={this.ratingBodyTemplate}></Column>
                        <Column header="Status" body={this.statusBodyTemplate}></Column>
                    </DataTable>
                </div>
            </div>
        );
    }
}
As you can see, this feature allows you to write custom templates for elements in a data table by defining the functions within the lements, and then referring to them in the 'body' prop of the column you want to use the template. My stab at a roughly equivalent cljs is (again, I'm trimming way down to show the part I'm having trouble with, so I know there are other parts that might not be working here):
(defn junk-button [_] [:> Button {:label "test"}])

(defn table []
  (let [del-button (r/as-element junk-button)]
    [:> DataTable {:value table-data
                    :on-row-reorder reorder 
                    :resizableColumns true}
      [:> Column {:header "Delete"
                  :body [del-button]}]]))
When I actually load info into the table, I get an error saying "Uncaught TypeError: this.props.body is not a function". Any ideas how to define a custom element in cljs here? Many thanks!!

p-himik 2021-06-15T10:45:25.109900Z

In the JSX example, you pass the function. In the CLJS code, you pass a vector - why? Just pass the function. Apart from that, it still won't work because Column expects that function to return a React element, but in your case it returns Hiccup. Wrap its result with reagent.core/as-element.

DFST 2021-06-15T13:43:44.112400Z

Thanks for the response. I think I had it in the vector because when I pass it in directly as you suggest, I get a different error: Uncaught Error: Objects are not valid as a React child (found: object with keys {ns, name, fqn, _hash, cljs$lang$protocol_mask$partition0$, cljs$lang$protocol_mask$partition1$}). If you meant to render a collection of children, use an array instead.

p-himik 2021-06-15T13:47:51.112800Z

That's exactly because you didn't use as-element.

DFST 2021-06-15T13:58:57.113200Z

no, it happens when I use as-element. Here is the code that throws the "Objects are not valid as React Child" error: (defn test [] (let [pdfs (-> @comp-state :pdfs vals) table-data (->> @comp-state :pdfs vals (sort-by :order) (map (fn [m] (dissoc m :file :data-url))) (map (fn [m] (assoc m :seq (nominal-order (:order m))))) clj->js) del-button (r/as-element junk-button)] [:div {:style {:min-height 350}} [:> DataTable {:ref (fn [e] (swap! comp-state assoc :dt e)) :value table-data :on-row-reorder reorder :resizableColumns true} [:> Column {:header "Delete" :body del-button}]]]))

p-himik 2021-06-15T14:00:00.113400Z

Because you used it wrong. It accepts Hiccup, not a function. https://github.com/reagent-project/reagent/blob/master/doc/InteropWithReact.md#example-function-as-child-components

p-himik 2021-06-15T14:00:59.113700Z

You have to use as-element inside junk-button. The function that you pass to Column as :body has to return a React element, and as-element creates a React element from a Hiccup vector.

DFST 2021-06-15T14:02:50.114300Z

Bingo! Thanks so much!!!!!!

1👍
p-himik 2021-06-15T10:45:25.109900Z

In the JSX example, you pass the function. In the CLJS code, you pass a vector - why? Just pass the function. Apart from that, it still won't work because Column expects that function to return a React element, but in your case it returns Hiccup. Wrap its result with reagent.core/as-element.

2021-06-15T12:08:14.110200Z

I have trouble understanding the r/track ...

2021-06-15T12:08:46.110300Z

as far as I understand it should work similarly to make-reaction, as the documentation itself suggests

2021-06-15T12:08:51.110500Z

but...

2021-06-15T12:10:21.110700Z

(defonce app-state (r/atom {:people {1 {:name "John Smith"} 2 {:name "Maggie Johnson"}}})) (defn people [] (print :fire 'people " ") (:people @app-state)) (defn person [id] (print :fire 'person " ") (-> @(r/track people) (get id))) (with-out-str (person 1)) ;; => ":fire person :fire people " (with-out-str (person 1)) ;; => ":fire person :fire people " (with-out-str (person 1)) ;; => ":fire person :fire people "

2021-06-15T12:11:06.110900Z

even though the atom hasn't changed, every time it fires all the functions along the way and it doesn't look like anything is cached

Karol Wójcik 2021-06-15T12:24:22.111200Z

I think the issue here is that person each time returns new instance of defer-able.

2021-06-15T13:06:12.112100Z

it looks like I don't understand reaction either, and I was almost sure I did

2021-06-15T13:07:39.112200Z

(def a (r/atom 10))

(def r1 (ra/reaction (let [n (rand-int @a)] n)))

@r1
;; => 6
@r1
;; => 3

DFST 2021-06-15T13:43:44.112400Z

Thanks for the response. I think I had it in the vector because when I pass it in directly as you suggest, I get a different error: Uncaught Error: Objects are not valid as a React child (found: object with keys {ns, name, fqn, _hash, cljs$lang$protocol_mask$partition0$, cljs$lang$protocol_mask$partition1$}). If you meant to render a collection of children, use an array instead.

p-himik 2021-06-15T13:47:20.112600Z

You can't rely on any reactive behavior outside of a reactive context. Views are such a context, when they're rendered by Reagent. Such as other reactions that are themselves used in a reactive context. But REPL is not a reactive context.

p-himik 2021-06-15T13:47:51.112800Z

That's exactly because you didn't use as-element.

2021-06-15T13:49:02.113Z

I'm pretty sure that's how I tested it in the past

DFST 2021-06-15T13:58:57.113200Z

no, it happens when I use as-element. Here is the code that throws the "Objects are not valid as React Child" error: (defn test [] (let [pdfs (-> @comp-state :pdfs vals) table-data (->> @comp-state :pdfs vals (sort-by :order) (map (fn [m] (dissoc m :file :data-url))) (map (fn [m] (assoc m :seq (nominal-order (:order m))))) clj->js) del-button (r/as-element junk-button)] [:div {:style {:min-height 350}} [:> DataTable {:ref (fn [e] (swap! comp-state assoc :dt e)) :value table-data :on-row-reorder reorder :resizableColumns true} [:> Column {:header "Delete" :body del-button}]]]))

p-himik 2021-06-15T14:00:00.113400Z

Because you used it wrong. It accepts Hiccup, not a function. https://github.com/reagent-project/reagent/blob/master/doc/InteropWithReact.md#example-function-as-child-components

p-himik 2021-06-15T14:00:59.113700Z

You have to use as-element inside junk-button. The function that you pass to Column as :body has to return a React element, and as-element creates a React element from a Hiccup vector.

p-himik 2021-06-15T14:01:23.113900Z

I just tested it in a reactive context. Works just fine.

(ns app.core
  (:require [reagent.core :as reagent]
            [reagent.ratom :as rea]
            [reagent.dom]
            [clojure.browser.dom]))

(defn view [a b]
  (reagent/with-let [x (rea/reaction (+ @a (rand-int 10000)))]
    [:div @x ";" @b]))

(defn app []
  (reagent/with-let [a (reagent/atom 0)
                     b (reagent/atom 0)]
    [:div
     [:button {:on-click #(swap! a inc)} "Inc A"]
     [:button {:on-click #(swap! b inc)} "Inc B"]
     [view a b]]))

(defn ^:export main []
  (reagent.dom/render [app] (clojure.browser.dom/get-element :app)))

p-himik 2021-06-15T14:01:44.114100Z

So if you press Inc B, the part before ; won't change, as expected.

DFST 2021-06-15T14:02:50.114300Z

Bingo! Thanks so much!!!!!!

1👍
2021-06-15T14:11:41.114600Z

Thx!