Using Tanstack Query from Clojurescript

Intro to Tanstack Query (a.k.a React Query)

Tanstack Query (a.k.a React Query) is a tool which is loved in the react ecosystem. It has many benefits like:

  1. Caching requests throughout pages
  2. Automatic re-fetching
  3. Re-fetching on focus
  4. Support for Suspense and server streaming

Problem

We cannot use it directly in ClojureScript because the CommonJS files that Tanstack Query uses contain the private property class notation.

This happens because ClojureScript uses the Google Closure Compiler behind the scenes to interact with the JS ecosystem. The Closure compiler doesn't support public or private class fiels and this is why you will get an error like this:

Failed to inspect file
  /node_modules/@tanstack/query-core/build/modern/onlineManager.cjs

Errors encountered while trying to parse file
  /node_modules/@tanstack/query-core/build/modern/onlineManager.cjs
  {:line 30, :column 2, :message "'}' expected"}

Solution

Step 1: Make required file changes

Go into node_modules/@tanstack/query-core/build/modern and replace all private fields from all the .cjs (commonjs) files:

class MyClass {
-  #privateField
+  privateField
}

Step 2: Use patch-package to create a permanent patch file

From your project's root directory, run:

npx patch package @tanstack/query-core

Note that we patch @tanstack/query-core and not @tanstack/react-query. The react query package uses query-core underneath since it targets multiple rendering libraries (Vue, Svelte etc.)

If everything goes well you should see

patch-package 8.0.0
• Creating temporary folder
• Installing @tanstack/query-core@5.40.0 with npm
• Diffing your files with clean files
✔ Created file patches/@tanstack+query-core+5.40.0.patch

And a directory patches should be created in your project root with the associated git patch inside. It should look something like this.

Step 3: Making changes permanent for your repository

I advise you use a static version of @tanstack/react-query. The solution above works for version 5.40.1:

  "dependencies": {
    ...
    "@tanstack/react-query": "5.40.1",
    ...
  }

Follow the steps outlined in the patch-package README:

  1. Add patch-package as a postinstall script in your package.json
  "scripts": {
   ...
    "postinstall": "patch-package"
  },

  1. Install patch-package as a dev dependency
npm i -D patch-package

Now every time you will run npm install, the git patch will be applied to make the library work correctly with ClojureScript.

Step 3: Import @tanstack/react-query to your app

(ns query
  (:require
   ["@tanstack/react-query" :as rq]))

(defn use-query
  "Wrap useQuery

  `query-key` query key array
  `query-fn` query function to fetch data"
  [{:keys [query-key query-fn]}]
  (let [result (rq/useQuery #js {:queryFn query-fn
                                 :queryKey (into-array query-key)})]
    {:data result.data
     :error? result.isError
     :success? result.isSuccess
     :loading? result.isLoading}))

(defonce query-client (rq/QueryClient.))

(defui query-client-provider
  [{:keys [children]}]
  ($ rq/QueryClientProvider {:client query-client}
     children))

(defhook use-get-user
  []
  (use-query {:query-key [:user]
              :query-fn get-user}))

Conclusion

(TanStack) react query is a great tool and should see more usage in the Clojure ecosystem however ClojureScript doesn't yet have full support for JS class syntax so we have to adapt in the meantime

Big thanks to Toni Väisänen for showing me the correct solution for this issue and big thanks to Juho Teperi (Reagent maintainer) for coming up with the original solution.

I hope this has been helpful to you.