Fork me on GitHub

Using hooks with reagent is a pain, using functional components gets you all kind of problems, for instance in props namespaced keywords and sets are not properly converted. Some hooks are completely useless like useState because a ratom replaces it easily, but others are unavoidable when working with some libraries. I figured that you can workaround the props issue by using a separate component that you add to your view with this syntax [:<> [myhook param1 param2] [rest-of-my-views]] Here is an example of this workaround for a rewrite of useScrollToTop from react-navigation, to handle an onPress event from a parent: clojure

(defn use-scroll-to-top-hook
  "Hook to scroll to top
  Takes an atom containing a ref to the flat list and scrolls to top
  when the view is focused and user presses on the home tab"
  (let [use-scroll-to-top
        (fn []
          (let [^js navigation (useNavigation)
                ^js route (useRoute)]
            ;; useEffect takes a function that returns a function that will
            ;; be called onDestroy, in this case addListener is returning
            ;; a function to unsubscribe to the listener
             (fn []
               (let [^js current
                     ;; we recursively loop through navigation parent until
                     ;; we get to the navigator that contains the home tab
                     ;; which emits the tabPress event we are interested in
                     ;; this is basically what the useScrollToTop hook from
                     ;; react-navigation does except we don't stop at the first
                     ;; parent
                     (loop [^js current navigation]
                       (if-not (and current
                                    (= "tab" (.-type ^js (.dangerouslyGetState current)))
                                    (= "home" (first ^js (.-routeNames ^js (.dangerouslyGetState current)))))
                         (recur (.dangerouslyGetParent current))
                  (fn [event]
                    (when (.isFocused navigation)
                       (fn []
                         (when-let [^js ref @!ref]
                           (.scrollToOffset ref (clj->js {:offset 0
                                                          :animated true}))))))))))))
    [:> use-scroll-to-top]))


Can someone help me with externs? I wrote this code to improve on the datatables recipe: After setting infer-externs & `warn-on-infer` I get these warnings: My understanding is that I'm supposed to provide type hints, but I don't know how to determine the types I'm supposed to hint eg "what is .destroy a member function of?"


IIRC The type hint can just say "this is a js object"/`^js`, and the names won't be munged


I added (defn refs [^js/React.Component component] (.-refs component)) and it looks like that removed one of my warnings. Now I need to do the same thing for .-main. Confession: I don't know what refs is or how to divine the type. Is there anything like (type thing) which actually gives me useful information in cljs? Because type isn't giving me what I want.


Does a simple ^js (i.e. without the /React.Component qualifier) work?


I get more warnings with that, so I'm guessing no.


Has anyone used antizer the wrapper around Ant.Design? I'm trying to look at an example of creating an editable table.


To try this a third way, the externs guide ( teaches you to type hint in order to produce externs. @dnolen goes through an example where he type hints wrap-baz with ^js/Foo.Bar, but doesn't show me how he determined that ^js/Foo.Bar is the type hint which was needed. How did he do that?

Patrick Truong19:04:07

Hello everyone, I’m new to ClojureScript and asked this question in the beginners channel, to which someone recommended me maybe asking this here: I’m working on a basic Reagent project using just shadow-cljs. I’m using cider nrepl and VS Code Calva. On my main app file, I have a simple atom for toggle:

(ns alpha-journal.core
   [reagent.core :as r]
   [reagent.dom :as dom]))

(def toggled (r/atom false))

(defn app []
  [:div {:class [(when true 'bg-red-500) 'h-screen]}
which is referenced in the app component. However, when I used my repl and lookup any variables or functions, it gives me the value, but also gives me a warning:
alpha-journal.core=> @toggled
------ WARNING - :undeclared-var -----------------------------------------------
 Resource: :1:2
 Use of undeclared Var alpha-journal.core/toggled
What does this warning mean? Why are my variables considered undeclared? How can I get fix this warning? I saw on the ClojureScript reference ( that I can set a compiler option to :warn-on-undeclared false but I’m not sure why I would need to do that in the first place. Thanks for all the help 🙂

Andres Moreno04:05:52

@U05224H0W Do you have any hints on this one? I am new to all of this (CLJS, shadow-cljs) and have the same question. Thanks.


I don't know. I suspect wrong REPL use


Here's a macro I quickly wrote to handle hooks and hocs. Tested it on @material-ui/styles withStyles and makeStyles

(defn ^:private extract-hooks
  "Provides hooksv and hooksjs to the defc macro."
   (fn [m k v]
     (let [sym (gensym 'hook)]
       (-> m
           (update :hooksv conj sym)
           (update :hooksv conj (list v))
           (assoc-in [:hooksjs k] sym))))
   {:hooksv []
    :hooksjs {}}
   (:hooks opts)))

(defn ^:private extract-hocs
  "Provides hocs to the defc macro."
  (map list (:hocs opts)))

(defmacro defc
  [name opts args & body]
  (let [opts (if (string? opts) {:doc opts} opts)
        {:keys [hooksjs hooksv]} (extract-hooks opts)
        hocs (extract-hocs opts)
        inner-sym1 (gensym 'inner)
        inner-sym2 (gensym 'inner)
        inner-sym3 (gensym 'inner)
        opts (-> opts
                 (dissoc :hooks)
                 (dissoc :hocs))]

       (defn- ~inner-sym3 ~args ~@body)

       (def ^{:private true}
         (-> (fn [args#]
               (into [~inner-sym3 (cljs.core/js->clj (dissoc args# :children) :keywordize-keys true)]
                     (:children args#)))

       (defn- ~inner-sym1
         (let ~hooksv
            (cljs.core/clj->js (merge ~hooksjs (goog.object/get args# "props")))
            (goog.object/get args# "children"))))

       (defn ~name
         [props# & children#]
         (let [has-props?# (map? props#)
               _children# (if has-props?# children# (conj children# props#))
               _props# (if has-props?# props# {})]
            (cljs.core/js-obj "props" _props# "children" _children#)))))))
Can be used like so:
(defc ctest
  {:hocs [(withStyles #js{:root #js{:color "green"}})]}
  [props & children]
  (into [:div {:class (-> props :classes :root)}] children))

(defn some-component []
  [ctest [:p "Text is green"]])

👏 4