T
TanStack11mo ago
correct-apricot

`queryClient.prefetchQuery` is not storing the data in cache when using in Next.js with RPC calls

I am using Next.js with Hono as the backend. Here's how I am prefetching the data on server component
const Posts = async () => {
const session = await auth()

if (!session?.user) {
return <Unauthorized />
}

const queryClient = new QueryClient()
await queryClient.prefetchQuery(getPostQueryOptions)

return (
<>
<Toaster />
<h1 className="scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl">
Posts
</h1>
<HydrationBoundary state={dehydrate(queryClient)}>
<PostList />
</HydrationBoundary>
<div>
<CreatePostForm userId={session?.user.id || ''} />
</div>
</>
)
}
const Posts = async () => {
const session = await auth()

if (!session?.user) {
return <Unauthorized />
}

const queryClient = new QueryClient()
await queryClient.prefetchQuery(getPostQueryOptions)

return (
<>
<Toaster />
<h1 className="scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl">
Posts
</h1>
<HydrationBoundary state={dehydrate(queryClient)}>
<PostList />
</HydrationBoundary>
<div>
<CreatePostForm userId={session?.user.id || ''} />
</div>
</>
)
}
Here's the hooks and Hono RPC calls
export const getPostQueryOptions = queryOptions({
queryKey: ['posts'],
queryFn: getPosts,
staleTime: 10 * 1000,
})

export function useGetPosts() {
return useQuery(getPostQueryOptions)
}
export const getPostQueryOptions = queryOptions({
queryKey: ['posts'],
queryFn: getPosts,
staleTime: 10 * 1000,
})

export function useGetPosts() {
return useQuery(getPostQueryOptions)
}
export async function getPosts(): Promise<
Array<Zod.infer<typeof selectPostSchema>>
> {
// const response = await fetch('/api/posts')
const response = await client.api.posts.$get()

if (!response.ok) {
throw new Error(`Request failed with status: ${response.status}`)
}

return response.json()
}
export async function getPosts(): Promise<
Array<Zod.infer<typeof selectPostSchema>>
> {
// const response = await fetch('/api/posts')
const response = await client.api.posts.$get()

if (!response.ok) {
throw new Error(`Request failed with status: ${response.status}`)
}

return response.json()
}
What am I missing
1 Reply
correct-apricot
correct-apricotOP11mo ago
Here's how I have setup react-query
// In Next.js, this file would be called: app/providers.tsx
'use client'

// Since QueryClientProvider relies on useContext under the hood, we have to put 'use client' on top
import {
isServer,
QueryClient,
QueryClientProvider
} from '@tanstack/react-query'
import React from 'react'

function makeQueryClient() {
return new QueryClient({
defaultOptions: {
queries: {
// With SSR, we usually want to set some default staleTime
// above 0 to avoid refetching immediately on the client
staleTime: 60 * 1000
}
}
})
}

let browserQueryClient: QueryClient | undefined = undefined

function getQueryClient() {
if (isServer) {
// Server: always make a new query client
return makeQueryClient()
} else {
// Browser: make a new query client if we don't already have one
// This is very important, so we don't re-make a new client if React
// suspends during the initial render. This may not be needed if we
// have a suspense boundary BELOW the creation of the query client
if (!browserQueryClient) browserQueryClient = makeQueryClient()
return browserQueryClient
}
}

export default function QueryProviders({
children
}: {
children: React.ReactNode
}) {
// NOTE: Avoid useState when initializing the query client if you don't
// have a suspense boundary between this and the code that may
// suspend because React will throw away the client on the initial
// render if it suspends and there is no boundary
const queryClient = getQueryClient()

return (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
)
}
// In Next.js, this file would be called: app/providers.tsx
'use client'

// Since QueryClientProvider relies on useContext under the hood, we have to put 'use client' on top
import {
isServer,
QueryClient,
QueryClientProvider
} from '@tanstack/react-query'
import React from 'react'

function makeQueryClient() {
return new QueryClient({
defaultOptions: {
queries: {
// With SSR, we usually want to set some default staleTime
// above 0 to avoid refetching immediately on the client
staleTime: 60 * 1000
}
}
})
}

let browserQueryClient: QueryClient | undefined = undefined

function getQueryClient() {
if (isServer) {
// Server: always make a new query client
return makeQueryClient()
} else {
// Browser: make a new query client if we don't already have one
// This is very important, so we don't re-make a new client if React
// suspends during the initial render. This may not be needed if we
// have a suspense boundary BELOW the creation of the query client
if (!browserQueryClient) browserQueryClient = makeQueryClient()
return browserQueryClient
}
}

export default function QueryProviders({
children
}: {
children: React.ReactNode
}) {
// NOTE: Avoid useState when initializing the query client if you don't
// have a suspense boundary between this and the code that may
// suspend because React will throw away the client on the initial
// render if it suspends and there is no boundary
const queryClient = getQueryClient()

return (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
)
}
Layout.tsx
export default function RootLayout({
children
}: Readonly<{
children: React.ReactNode
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<QueryProviders>
<SessionProvider>{children}</SessionProvider>
<ReactQueryDevtools initialIsOpen={false} />
</QueryProviders>
</body>
</html>
)
}
export default function RootLayout({
children
}: Readonly<{
children: React.ReactNode
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<QueryProviders>
<SessionProvider>{children}</SessionProvider>
<ReactQueryDevtools initialIsOpen={false} />
</QueryProviders>
</body>
</html>
)
}

Did you find this page helpful?