reagent

A minimalistic ClojureScript interface to React.js http://reagent-project.github.io/
greensponge 2020-10-26T13:37:17.092800Z

Hello everyone, has anyone else tried using ag-grid-react with infinite scrolling? I can't figure out why it doesn't link up the rows correctly, it ends up drawing 100 empty rows that is scrollable. I'll try to shorten the data for your convinience:

(ns example (:require
             [reagent.core :as r]
             ["ag-grid-react" :as ag-grid :refer (AgGridReact)]
             [cljs-http.client :as http]
             [cljs.core.async :refer [<!]]))
    
(def ag-adapter (r/adapt-react-class AgGridReact))

(defn create-grid
  "I'm in a DIV somewhere, the actual grid component that the user interacts with"
  []
  [ag-adapter {:gridOptions defaultGridOptions}])

(def defaultGridOptions {:rowModelType "infinite" ; if this is not defined, datasource with getRows won't work.
                         :columnDefs  col-data ; looks like it's drawing the columns up correctly
                         :datasource {:getRows get-rows}}) ; passes params to function correctly

(def col-data
     "This correctly creates a grid with "Id" and "Description" columns, even creates the filter options correctly, it seems to work"
     [{ :headerName "Id" :field "id" :filter "agNumberColumnFilter" }
      { :headerName "Description" :field "description" }

(defn get-rows [params]
  ;(prn params) ; Is a #js object with expected values filled in from ag-grid component
  (go (let [response (<! (http/post "valid-endpoint"
                                    {:with-credentials? false
                                     :json-params {
                                                   :startRow (goog.object/getValueByKeys params #js ["startRow"])
                                                   :endRow (goog.object/getValueByKeys params #js ["endRow"])
                                                   :filterModel (goog.object/getValueByKeys params #js ["filterModel"])
                                                   :sortModel (goog.object/getValueByKeys params #js ["sortModel"])}}))]
        ;(prn (:rows (:body response))) ; This prints expected data, albeit in clojure form (e.g. [{ :id 1 :description "some example" }] )
        (js-invoke params "successCallback" (:rows (:body response)) (:lastRow (:body response)))))) ; What I think this means in JS: params.successCallback(rows, lastRow)
Context: I am using an endpoint that I know returns the correct data for an ag-grid, I'm evaluating CLJS by trying to convert a small part of an existing app to work in it. More info: Scrolling in the component causes POST callbacks that return (seemingly) correct data in the console / network tab. (P.S. If I'm doing something in a stupid way, let me know.)

greensponge 2020-10-27T08:40:06.113100Z

To complete this answer for possible future readers, this was the missing piece of the puzzle and it ended up working perfectly when I had time to try it today (although as stated above, it would be even better to return the correct data response directly):

(.successCallback ^js params (clj->js (:rows (:body response))) (clj->js (:lastRow (:body response)))))))
For reference, the above ag-grid call is equivalent to this in JS:
params.successCallback(rows, lastRow);
Thanks again, @p-himik!

greensponge 2020-10-26T13:50:02.094600Z

And if no one has used ag-grid-react in this way before-> then without knowledge of its API, can you see if I'm doing something dumb in get-rows?

p-himik 2020-10-26T14:17:06.094700Z

Are (:rows (:body response)) and (:lastRow (:body response)) plain JS objects? Because I would not expect successCallback to work with CLJS objects. Apart from that, you shouldn't use js-invoke for function names that are proper identifiers. Just use (.successCallback ^js params ...).

greensponge 2020-10-26T14:42:01.095100Z

Thanks for the tip @p-himik, yes the API returns a JSON object that looks something like this in the old app (TypeScript):

export interface ISomeGrid {
  lastRow: number;
  rows: ISomeData[]; // imagine it being of format { id: 1, description: "some description"} for this example
}
I tried "un-clojuring" the response by doing this:
(.stringify js/JSON (clj->js (:rows (:body response))))
But it doesn't seem to improve my situation, is there some better way to convert the response into JSON for this successCallback to consume?

p-himik 2020-10-26T14:46:34.095300Z

Don't call stringify, just call (clj->js (:rows (:body response))). It would be even better if you could tell cljs-http to stop automatically converting JS objects into CLJ data.

👍 1
Nathan 2020-10-26T21:54:58.098Z

Hello there, does anybody know how to use ANT design subcomponents in Reagent? For example, in javascript I'd import subcomponents like this:

import { Layout, Menu, Breadcrumb } from 'antd';

const { Header, Content, Footer } = Layout;
and then I could use them like this: <Header /> The parent component (Layout) works fine, but I can't manage to use the subcomponents. I tried [:> Layout.Header ] but it doesn't work.

Nathan 2020-10-26T21:56:24.098100Z

I also tried to destructure the subcomponents in a let binding like this:

(defn authenticated
  []
  (let [{Header :Header} Layout]
    (js/console.log Header)
    [:> Layout
     [:> Header
      [:Menu {:theme               "dark"
              :mode                "horizontal"
              :defaultSelectedKeys ["1"]}
       ;[:Menu.Item {:key "1"} "nav 1"]
       ;[:Menu.Item {:key "2"} "nav 2"]
       ;[:Menu.Item {:key "3"} "nav 3"]
       ]
      ]
     [:> Layout.Content
      [:h1 "Main Content"]]
     [:> Layout.Footer
      [:p "Footer"]]
     ])
  )
but no success

p-himik 2020-10-26T21:57:35.098300Z

The issue is in your let. Layout is a JS object, you cannot destructure it.

1
p-himik 2020-10-26T21:58:15.098500Z

I'm also not sure what those :Menu and :Menu.Item are. They're keywords and I don't think they're valid (or at least regular) HTML tags.

Nathan 2020-10-26T22:00:28.098700Z

Ah it is an ant ui component, I already removed it because I was just testing. Here is a simpler example:

(ns app.nav.views.authenticated
  (:require
    ["antd" :refer [Layout]]))

(defn authenticated
  []
    [:> Layout
     [:> Layout.Header
     ]]
  )

Nathan 2020-10-26T22:00:45.098900Z

but I can't manage to use the Header component that is a subcomponent of Layout

p-himik 2020-10-26T22:02:15.099200Z

Does (js/console.log Layout.Header) actually print anything?

Nathan 2020-10-26T22:06:26.099400Z

When I try to log Layout.Header I get this error: Use of undeclared Var app.nav.views.authenticated/Layout

Nathan 2020-10-26T22:06:45.099600Z

This is the current code I'm running:

(ns app.nav.views.authenticated
  (:require
    ["antd" :refer [Layout]]))

(defn authenticated
  []
  [:> Layout
   (js/console.log Layout.Header)
   [:h1 "OK"]]
  )

Nathan 2020-10-26T22:07:37.099800Z

I'm able to use the Layout component without any problems, but as soon as I try to log or use any of the subcomponents I get the error above. I'm using shadow-cljs.

p-himik 2020-10-26T22:12:52.100100Z

Try (def Header (.-Header ^js Layout)).

Nathan 2020-10-26T22:14:44.100300Z

It works! :star-struck: Thanks a lot

👍 1
Nathan 2020-10-26T22:15:07.100600Z

What the ^js means? I never saw this before.

Nathan 2020-10-26T22:16:07.101Z

Perfect. Thank you very much!

Nathan 2020-10-26T22:20:12.101200Z

It also works without the ^js

p-himik 2020-10-26T22:21:30.101400Z

It may break with :advanced optimizations. Maybe imports are protected from this, but I've never seen mentions of it.

p-himik 2020-10-26T22:22:13.101600Z

Ah, there it is: "No hint required for :as and :refer bindings"

p-himik 2020-10-26T22:22:27.101800Z

So yeah, seems like you can omit ^js when working with imported stuff.

Nathan 2020-10-26T22:23:59.102Z

Cool! Good to know 🙏