Last active
October 4, 2024 19:07
-
-
Save Deraen/24a877421f3d9012d45e480bfe885156 to your computer and use it in GitHub Desktop.
Cljs and TanStack Query
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns foo.queries | |
(:require [foo.query :as q]) | |
;; I suggest having a simple JS Fetch wrapper or something. | |
;; Maybe one which works with route-names (from Reitit route-data?) and renders the paths | |
;; with given parameters. | |
(defn get-todo-query [id] | |
#js {:queryKey #js ["todo" id] | |
:enabled id | |
:queryFn (fn [_ctx] (fetch (url :api/todo {:id id})))}) | |
(defn remove-todo-mutation [id] | |
#js {:queryFn (fn [_ctx] (fetch (url :api/remove-todo {:id id}) {:method :delete})) | |
:onSuccess (fn [_] (q/invalidate-queries {:queryKey ["todo" id])}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns foo.query | |
(:require ["@tankstack/react-query"] | |
[cljs-bean.core :refer [->clj ->js bean])) | |
;; Check https://github.com/metosin/shadow-cljs-esbuild for ESBuild | |
;; example because Closure-compiler can't handle TSQ | |
;; Idea of using ->clj select option is to keep JSON responses in TSQ store | |
;; as JS objects, because TSQ devtool doesn't really handle Clj datastructures | |
;; and it can't be extended. | |
;; NOTE: This doesn't work with Transit responses as those would already be Cljs data | |
(def query-client (query/QueryClient. | |
#js {:defaultOptions #js {:queries #js {:select ->clj}}})) | |
;; Non-recursive lazy transform to Clj | |
;; Cljs-bean should also correctly handle TSQ tracked properties: | |
;; https://tanstack.com/query/latest/docs/framework/react/guides/render-optimizations#tracked-properties | |
;; Meaning that only changes to the properties that are really used (accessed/destructured from the result), | |
;; will trigger the component to re-render. | |
(defn use-query | |
[query] | |
(bean (query/useQuery query))) | |
;; Add some other wrappers, e.g. invalidate-queries with your query-client. | |
(defn invalidate-queries | |
[partial-query] | |
(.invalidateQueries query-client (->js partial-query))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns foo.re-frame-use | |
(:require [re-frame.core :as rf]) | |
;; EXPERIMENTAL | |
;; Not sure how good of an idea this is | |
(rf/reg-sub ::test-sub | |
(fn [[_ id]] | |
;; Signal fn is only called by re-frame when the parameters/sub vector changes, | |
;; so this only creates the query JS object once -> :use-query and | |
;; the Observer is only initialized once (unless id value changes). | |
(rf/subscribe [:use-query (q/get-todo-query {:id id})])) | |
(fn [todo _] | |
todo)) | |
(rf/reg-event-fx ::test-event | |
(rf/inject-cofx :get-query-data (fn [[_ id]] | |
["todo" id])) | |
(fn [{:keys [queries]} _] | |
(js/console.log (get queries ["todo" 9])) | |
nil)) | |
(rf/dispatch [::test-event 9])) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns foo.re-frame | |
(:require [re-frame.core :as rf]) | |
;; EXPERIMENTAL | |
;; Not sure how good of an idea this is | |
;; Experimental Re-frame query that can be used to initialize and access | |
;; TSQ queries from Re-frame subscriptions. | |
;; Query lifecycle is tied to the Reaction lifecycle, | |
;; if the subscription is being used in a component, | |
;; the query is active in TSQ. | |
;; | |
;; Query value is the same as what `useQuery` would take. | |
;; Either cljs (will be converted) or the JS value directly. | |
(rf/reg-sub-raw :use-query | |
(fn [_app-db [_ query]] | |
(let [;; could also store to app-db here, using queryKey as path | |
store (r/atom nil) | |
observer (query/QueryObserver. query-client (->js query)) | |
unsub (.subscribe observer (fn [result] | |
;; This stores the full response object, not just the response data | |
(reset! store result)))] | |
(ratom/make-reaction | |
(fn [] | |
@store) | |
:on-dispose | |
(fn [] | |
(unsub)))))) | |
;; Experimental cofx to get a TSQ query response into a Re-frame event handler. | |
;; Because this has to be synchronous and the result has to be available, | |
;; this doesn't run the query, just gets the data if it is already available, | |
;; from useQuery. | |
;; | |
;; Note: the data is available in coeffect map under | |
;; :queries key, using query-key as a map key. | |
(rf/reg-cofx :get-query-data | |
(fn [coeffects param] | |
(let [query-key (cond | |
(ifn? param) (param (:event coeffects)) | |
:else param) | |
data (.getQueryData query-client (->js query-key))] | |
;; NOTE: The data is JS object, could call `bean` here, which is our | |
;; default select fn. | |
(update coeffects :queries assoc (->clj query-key) data)))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment