T
TanStack3y ago
provincial-silver

Why does SSR with Next.js runs queries twice?

I have the following code following the official docs and I constantly see two requests. Why? Isn't supposed the dehydrate pass the data automagically? - I am using also the _app.tsx config from the docs (https://tanstack.com/query/v4/docs/react/guides/ssr) Code:
import Head from 'next/head'
import styles from '@/styles/Home.module.css'
import { useGetLocationsQuery } from '@/generated/types'
import { dehydrate, QueryClient } from '@tanstack/react-query'

export default function Home(props: any) {
console.log('props', props)
const { data } = useGetLocationsQuery({})
return (
<>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
{data?.allLocations?.nodes?.map((location) => (
<div key={location.id}>
<h1>{location.name}</h1>
<p>{location.slug}</p>
</div>
))}
</main>
</>
)
}

export async function getServerSideProps() {
const queryClient = new QueryClient()
await queryClient.prefetchQuery(
useGetLocationsQuery.getKey(),
useGetLocationsQuery.fetcher()
)
return {
props: {
dehydratedState: dehydrate(queryClient),
},
}
}
import Head from 'next/head'
import styles from '@/styles/Home.module.css'
import { useGetLocationsQuery } from '@/generated/types'
import { dehydrate, QueryClient } from '@tanstack/react-query'

export default function Home(props: any) {
console.log('props', props)
const { data } = useGetLocationsQuery({})
return (
<>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
{data?.allLocations?.nodes?.map((location) => (
<div key={location.id}>
<h1>{location.name}</h1>
<p>{location.slug}</p>
</div>
))}
</main>
</>
)
}

export async function getServerSideProps() {
const queryClient = new QueryClient()
await queryClient.prefetchQuery(
useGetLocationsQuery.getKey(),
useGetLocationsQuery.fetcher()
)
return {
props: {
dehydratedState: dehydrate(queryClient),
},
}
}
When I see the logs on the API, I get 2/3 requests everytime I reload.
SSR | TanStack Query Docs
React Query supports two ways of prefetching data on the server and passing that to the queryClient. Prefetch the data yourself and pass it in as initialData
8 Replies
quickest-silver
quickest-silver3y ago
SSR | TanStack Query Docs
React Query supports two ways of prefetching data on the server and passing that to the queryClient. Prefetch the data yourself and pass it in as initialData
provincial-silver
provincial-silverOP3y ago
I don't see it clear, I'm using hydration and I expect only one request to the db on initial load. The one used by the getServerSideProps. Is that correct? In my code I'm replicating what you sent @TkDodo 🔮 I'm trying to play with staletime, still it doesn't work. Should I load initial data from the dehydrated query? @TkDodo 🔮 ? I think the docs are wrong, the dehydrated stated is not passed automatically to the query you use later on, you have also to pass initialData
quickest-silver
quickest-silver3y ago
It is if you use the Hydrate component
provincial-silver
provincial-silverOP3y ago
Ah, so hydrate is meant to always run once in server + once in client? My goal is to not have it run twice. But have the benefit of the sub-queries Should I combine initialData + hydrate to have it fully working as I expect? Meaning having only one request and cache in sub-components that use the same query? cc @TkDodo 🔮 - Could you clarify please? I still don't get it 😅
quickest-silver
quickest-silver3y ago
please show your app.tsx my guess is you are not using the <Hydrate> component - dehydrate runs on the server, you fetch there, then pass it to app.tsx as a prop - from there, you pass that prop to the <Hydrate> component, which will put those queries into the query cache you have on the client with that, you don't need initialData, because data is already in the cache after that if you see another fetch, it should be a background refetch, because of staleness. Set staleTime to avoid it
provincial-silver
provincial-silverOP3y ago
This is my app.tsx:
import '../globals.css'
import type { AppProps } from 'next/app'
import {
Hydrate,
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query'
import React from 'react'

export default function App({ Component, pageProps }: AppProps) {
const [queryClient] = React.useState(() => new QueryClient())

return (
<QueryClientProvider client={queryClient}>
<Hydrate state={pageProps.dehydratedState}>
<Component {...pageProps} />
</Hydrate>
</QueryClientProvider>
)
}
import '../globals.css'
import type { AppProps } from 'next/app'
import {
Hydrate,
QueryClient,
QueryClientProvider,
} from '@tanstack/react-query'
import React from 'react'

export default function App({ Component, pageProps }: AppProps) {
const [queryClient] = React.useState(() => new QueryClient())

return (
<QueryClientProvider client={queryClient}>
<Hydrate state={pageProps.dehydratedState}>
<Component {...pageProps} />
</Hydrate>
</QueryClientProvider>
)
}
When I do a console.log on I can see the dehydrateQuery, though the requests still happens twice, once on the server and once on the client:
import { dehydrate, QueryClient, useQuery } from '@tanstack/react-query'
import { useGetLocationsQuery } from '../graphql/generated'

export default function Home(props: any) {
const { data } = useQuery({
queryKey: useGetLocationsQuery.getKey(),
queryFn: useGetLocationsQuery.fetcher(),
})

console.log('props', props)
console.log('data', data)

return <div>Hi</div>
}

export async function getServerSideProps() {
const queryClient = new QueryClient()
await queryClient.prefetchQuery({
queryKey: useGetLocationsQuery.getKey(),
queryFn: useGetLocationsQuery.fetcher(),
})

return {
props: { dehydratedState: dehydrate(queryClient) },
}
}
import { dehydrate, QueryClient, useQuery } from '@tanstack/react-query'
import { useGetLocationsQuery } from '../graphql/generated'

export default function Home(props: any) {
const { data } = useQuery({
queryKey: useGetLocationsQuery.getKey(),
queryFn: useGetLocationsQuery.fetcher(),
})

console.log('props', props)
console.log('data', data)

return <div>Hi</div>
}

export async function getServerSideProps() {
const queryClient = new QueryClient()
await queryClient.prefetchQuery({
queryKey: useGetLocationsQuery.getKey(),
queryFn: useGetLocationsQuery.fetcher(),
})

return {
props: { dehydratedState: dehydrate(queryClient) },
}
}
Could it be the staleTime or anyother missing options?
quickest-silver
quickest-silver3y ago
yes, it's staleTime. I think I said this 3 times now 🙂
provincial-silver
provincial-silverOP3y ago
God, you're right. 🙏 sorry and thanks!

Did you find this page helpful?