Instead of polling you subscribe to receive update from server-side query functions in a Convex deployment. Convex is a database with server-side (db-side? like stored procedures) functions that update reactively.
New results for all relevant subscriptions are pushed to the client where they
update at the same time so data is never stale and there's no need to call
queryClient.invalidateQueries()
.
See ./src/example.tsx for a real example. The general pattern:
- Create a ConvexClient and ConvexQueryClient. Set the global default
queryKeyHashFn
toconvexQueryClient.hashFn()
andqueryFn
toconvexQueryClient.queryFn()
. Connect the ConvexQueryClient to the React Query QueryClient.
const convexClient = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL);
const convexQueryClient = new ConvexQueryClient(convexClient);
const queryClient = new QueryClient({
defaultOptions: {
queries: {
queryKeyHashFn: convexQueryClient.hashFn(),
queryFn: convexQueryClient.queryFn(),
},
},
});
convexQueryClient.connect(queryClient);
- Use
useQuery()
with theconvexQuery
options factory function called with anapi
object imported from../convex/_generated/server
and the arguments for this query function. These two form the query key.
const { isPending, error, data } = useQuery({
...convexQuery(api.repos.get, { repo: "made/up" }),
gcTime: 10000, // unsubscribe after 10s of no use
});
staleTime
is set to Infinity
beacuse this data is never stale; it's
proactively updated whenever the query result updates on the server. (see
tkdodo's post
for more about this) If you like, customize the gcTime
to the length of time a
query subscription should remain active after all useQuery()
hooks using it
have unmounted.
If you need to use a Convex Action as a query, it won't be reactive; you'll get all the normal tools from React Query to refetch it.
New query results are pushed from the server, so a staleTime
of Infinity
should be used.
Your app will remain subscribed to a query until the gcTime
has elapsed. Tune
this for your app: it's usually a good tradeoff to use a value of at least a
couple seconds.
To run this example:
npm install
npm run dev
If you wrap your app in a ConvexProvider
you'll be able to use convex hooks
like useConvexMutation
and useConvexAction
.:
<ConvexProvider client={convex}>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</ConvexProvider>
You can use this mutation function directly or wrap it in a TanStack Query
useMutation
:
const mutationFn = useConvexMutation(api.board.createColumn);
const { mutate } = useMutation({ mutationFn });
const { mutate } = useMutation({
mutationFn: useConvexAction(api.time.getTotal),
});
TanStack Query isn't opionated about auth; an auth code might be a an element of a query key like any other. With Convex it's not necessary to add an additional key for an auth code; auth is an implicit argument to all Convex queries and these queries will be retried when authentication info changes.
Convex auth is typically done via JWT: some query functions will fail if
requested before calling convexReactClinet.setAuth()
with a function that
provides the token.
Auth setup looks just like it's recommended in
Convex docs, which make use of components that
use native convex hooks. For Clerk, this might look like this: a ClerkProvider
for auth, a ConvexProviderWithClerk
for the convex client, and a
QueryClient
.
<ClerkProvider publishableKey="pk_test_...">
<ConvexProviderWithClerk client={convex} useAuth={useAuth}>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</ConvexProviderWithClerk>
</ClerkProvider>
See the Convex Auth docs for setup instructions.
- auth
- paginated queries
- cleanup / unsubscribe in useEffect; something with hot reloading may not be working right
After cloning this repo run npm i
to install dependencies. This package uses
tshy to publish an ESM. If there's ever demand
for a CJS build we can add "cjs" to the "dialects" section of "tshy" config in
the package.json.
To publish an alpha release, update the version in package.json to something
like 0.0.0-alpha.1
and run npm publish --tag alpha
.
To publish a regular release, update the version in package.json to something
like 0.1.2
and run npm publish
.