T
TanStack3y ago
unwilling-turquoise

Query Key disappearing NextJS DevTools

I'm building a NextJS app leveraging react-query. When I load a page, it looks like the query key loads, but then instantly disappears. My guess is this has something to do with the query client going away, but I can't figure out what would be causing that. https://gyazo.com/98a07d34919ee4bb7ece7cfe67f4c20e Anyone see anything in this code that is immediately obvious?
import { Flex, Heading } from '@chakra-ui/react'
import { NextPage } from 'next'
import React, { ReactNode } from 'react'
import { useRouter } from 'next/router'
import { useAtom } from 'jotai'
import { activeAppIdAtom } from '../../../components/Sidebar'
import { useUserApplications } from '../../../lib/hooks/useUserApplications'
import { ReactElement } from 'react-markdown/lib/react-markdown'
import { NextPageWithLayout } from '../../_app'

type Props = {}

const Dashboard : NextPageWithLayout = ({

}: {}) => {

const router = useRouter()
const {appSlug} = router.query


const [
activeApplicationId,
setActiveApplicationId
] = useAtom(activeAppIdAtom)

const userApplications = useUserApplications()
//const activeApplication = userApplications.data?.find(app => app.id === activeApplicationId)


return (
<Flex minH='100vh' direction={'column'} gap='4'>
{appSlug}
<br/>
{activeApplicationId}
{/* <ApplicationSwitcher /> */}
<Heading>Apps</Heading>
{JSON.stringify(userApplications)}
<Flex>
{/*
{JSON.stringify(activeApplication)}
*/}
</Flex>
</Flex>
)
}

// Dashboard.getLayout = function getLayout(page){
// return (
// <Layout>

// {page}

// </Layout>
// )
// };

export default Dashboard
import { Flex, Heading } from '@chakra-ui/react'
import { NextPage } from 'next'
import React, { ReactNode } from 'react'
import { useRouter } from 'next/router'
import { useAtom } from 'jotai'
import { activeAppIdAtom } from '../../../components/Sidebar'
import { useUserApplications } from '../../../lib/hooks/useUserApplications'
import { ReactElement } from 'react-markdown/lib/react-markdown'
import { NextPageWithLayout } from '../../_app'

type Props = {}

const Dashboard : NextPageWithLayout = ({

}: {}) => {

const router = useRouter()
const {appSlug} = router.query


const [
activeApplicationId,
setActiveApplicationId
] = useAtom(activeAppIdAtom)

const userApplications = useUserApplications()
//const activeApplication = userApplications.data?.find(app => app.id === activeApplicationId)


return (
<Flex minH='100vh' direction={'column'} gap='4'>
{appSlug}
<br/>
{activeApplicationId}
{/* <ApplicationSwitcher /> */}
<Heading>Apps</Heading>
{JSON.stringify(userApplications)}
<Flex>
{/*
{JSON.stringify(activeApplication)}
*/}
</Flex>
</Flex>
)
}

// Dashboard.getLayout = function getLayout(page){
// return (
// <Layout>

// {page}

// </Layout>
// )
// };

export default Dashboard
Inside of useUserApplications
import { Application } from '@prisma/client';
import { useQuery } from '@tanstack/react-query'
import { fetcher } from '../apiClient'

const fetchApplications = () : Promise<Application[]> => {
return fetcher({
url: '/api/applications'
});
}

export const useUserApplications = () => {
return useQuery({
queryKey: ['userApplications'],
queryFn: fetchApplications
});
}
import { Application } from '@prisma/client';
import { useQuery } from '@tanstack/react-query'
import { fetcher } from '../apiClient'

const fetchApplications = () : Promise<Application[]> => {
return fetcher({
url: '/api/applications'
});
}

export const useUserApplications = () => {
return useQuery({
queryKey: ['userApplications'],
queryFn: fetchApplications
});
}
Gyazo
Gyazo
14 Replies
unwilling-turquoise
unwilling-turquoiseOP3y ago
Ok, something strange seems to be happening here. I stripped down the component so that it's now just the following. And it looks like if I include const router = useRouter() Then the dev tools crashes. Has anyone else seen anything like this before? actually disregard the above, it looks like even removing router it still is empty - though I can see that the data loaded This is how I'm importing the devtools panel:
export default function App({
Component,
pageProps: { session, ...pageProps }
}: AppPropsWithLayout) {

const queryClient = new QueryClient();
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout || ((page) => page)

return (
<SessionProvider session={session}>

<QueryClientProvider client={queryClient}>
<ReactQueryDevtoolsPanel />
<ChakraProvider>
{getLayout(<Component {...pageProps} />)}
</ChakraProvider>
</QueryClientProvider>
</SessionProvider>
)
}
export default function App({
Component,
pageProps: { session, ...pageProps }
}: AppPropsWithLayout) {

const queryClient = new QueryClient();
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout || ((page) => page)

return (
<SessionProvider session={session}>

<QueryClientProvider client={queryClient}>
<ReactQueryDevtoolsPanel />
<ChakraProvider>
{getLayout(<Component {...pageProps} />)}
</ChakraProvider>
</QueryClientProvider>
</SessionProvider>
)
}
full code with the imports for _app.tsx
import '../styles/globals.css'
import type { AppProps } from 'next/app'
import { ChakraProvider } from '@chakra-ui/react'
import { SessionProvider } from "next-auth/react"
import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools'


import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactElement, ReactNode } from 'react';
import { NextPage } from 'next';

export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
getLayout?: (page: ReactElement) => ReactNode
}

type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout
}

export default function App({
Component,
pageProps: { session, ...pageProps }
}: AppPropsWithLayout) {

const queryClient = new QueryClient();
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout || ((page) => page)

return (
<SessionProvider session={session}>

<QueryClientProvider client={queryClient}>
<ReactQueryDevtoolsPanel />
<ChakraProvider>
{getLayout(<Component {...pageProps} />)}
</ChakraProvider>
</QueryClientProvider>
</SessionProvider>
)
}
import '../styles/globals.css'
import type { AppProps } from 'next/app'
import { ChakraProvider } from '@chakra-ui/react'
import { SessionProvider } from "next-auth/react"
import { ReactQueryDevtoolsPanel } from '@tanstack/react-query-devtools'


import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactElement, ReactNode } from 'react';
import { NextPage } from 'next';

export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
getLayout?: (page: ReactElement) => ReactNode
}

type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout
}

export default function App({
Component,
pageProps: { session, ...pageProps }
}: AppPropsWithLayout) {

const queryClient = new QueryClient();
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout || ((page) => page)

return (
<SessionProvider session={session}>

<QueryClientProvider client={queryClient}>
<ReactQueryDevtoolsPanel />
<ChakraProvider>
{getLayout(<Component {...pageProps} />)}
</ChakraProvider>
</QueryClientProvider>
</SessionProvider>
)
}
unwilling-turquoise
unwilling-turquoiseOP3y ago
Ok I think I solved my issue, but would love some thoughts on why this is needed. When instantiating the App in _app.tsx, I needed to save the query client in state, otherwise it seems like what was likely happening is that the App would re-instantiate the query client on every render.
const [queryClient] = useState(() => new QueryClient());
const [queryClient] = useState(() => new QueryClient());
Can someone provide some more context on why this is needed in the NextJS instance, but not in the Get Started, Quick Start example: https://tanstack.com/query/latest/docs/react/quick-start What is NextJS doing here that requires the client to be saved via useState?
Quick Start | TanStack Query Docs
This code snippet very briefly illustrates the 3 core concepts of React Query: Queries
metropolitan-bronze
metropolitan-bronze3y ago
Have you tried defining the query client in module scope (lifted up, outside of the component body)? It looks like you’re conflating query client instantiation with a call to the useQueryClient hook If you instantiate the query client in a component it’ll be reinstantiated every time the component renders
unwilling-turquoise
unwilling-turquoiseOP3y ago
@Louis most of the demos I've seen online do the query client instantiation within the body of components though .... and i'm wrong
metropolitan-bronze
metropolitan-bronze3y ago
I don’t think that’s correct. Even the example you sent doesn’t do that
unwilling-turquoise
unwilling-turquoiseOP3y ago
I just looked at the example above again yah .... I'm wrong
metropolitan-bronze
metropolitan-bronze3y ago
Try my suggestion and let me know if it works for you
unwilling-turquoise
unwilling-turquoiseOP3y ago
apologies I see now this is the case, will do 🙂
metropolitan-bronze
metropolitan-bronze3y ago
No worries :reactquery:
unwilling-turquoise
unwilling-turquoiseOP3y ago
That worked well! thanks @Louis !
metropolitan-bronze
metropolitan-bronze3y ago
Awesome, you’re welcome 🙂
ambitious-aqua
ambitious-aqua3y ago
With next, client should be inside App to avoid accidentally sharing things between users when doing SSR, but it has to be stable (state or ref). It's in the docs! And in my faq blog 😅
metropolitan-bronze
metropolitan-bronze3y ago
@NicoNico I've accidentally misled you here, I'm really sorry! I'm not an avid Next user and was looking at differences between the code you sent and the example we were looking at on the docs; my apologies 🤦‍♂️ What Dominik said makes a lot of sense. Modules are evaluated once and the query client would/could persist between pre-renders for different users - thanks for correcting 👍
foreign-sapphire
foreign-sapphire3y ago
@LouisIt worked for me but is it good to use page router of nextjs with react query?

Did you find this page helpful?