T
TanStack13h ago
rival-black

Invalidating router context cache with supabase integration

Hello, I face a problem to reinvalidate router context when going to login page via URL. The issue is caused by lack of reinvalidation of react router context. In my setup I have an Auth context provider with effect
useEffect(() => {
supabase.auth.getSession().then(({ data: { session } }) => {
setUser(session?.user)
setIsAuthenticated(!!session?.user)
})

const {
data: { subscription },
} = supabase.auth.onAuthStateChange((_event, session) => {
setUser(session?.user)
setIsAuthenticated(!!session?.user)
})

return () => subscription.unsubscribe()
}, [])
useEffect(() => {
supabase.auth.getSession().then(({ data: { session } }) => {
setUser(session?.user)
setIsAuthenticated(!!session?.user)
})

const {
data: { subscription },
} = supabase.auth.onAuthStateChange((_event, session) => {
setUser(session?.user)
setIsAuthenticated(!!session?.user)
})

return () => subscription.unsubscribe()
}, [])
Sadly here I cannot revalidate context after setting the user state - this provider lives outside of router provider. Do you know how possibly I could make the context revalide after the user session is checked so that the Login component is not rendered?
export const Route = createFileRoute('/login')({
component: LoginPage,
beforeLoad: ({ context: { auth }, search }) => {
if (auth.isAuthenticated) {
throw redirect({ to: '/' })
}
},
})
export const Route = createFileRoute('/login')({
component: LoginPage,
beforeLoad: ({ context: { auth }, search }) => {
if (auth.isAuthenticated) {
throw redirect({ to: '/' })
}
},
})
6 Replies
rival-black
rival-blackOP13h ago
My Router Provider setup looks like following?
export function RouterProvider() {
const { isAuthenticated } = useAuth()

return (
<TanstackRouterProvider
router={router}
context={{
queryClient,
auth: {
isAuthenticated,
},
}}
/>
)
}
export function App() {
return (
<ThemeProvider defaultTheme="dark">
<SupabaseAuthProvider>
<TanstackQueryProvider>
<RouterProvider />
</TanstackQueryProvider>
</SupabaseAuthProvider>
</ThemeProvider>
)
}
export function RouterProvider() {
const { isAuthenticated } = useAuth()

return (
<TanstackRouterProvider
router={router}
context={{
queryClient,
auth: {
isAuthenticated,
},
}}
/>
)
}
export function App() {
return (
<ThemeProvider defaultTheme="dark">
<SupabaseAuthProvider>
<TanstackQueryProvider>
<RouterProvider />
</TanstackQueryProvider>
</SupabaseAuthProvider>
</ThemeProvider>
)
}
quickest-silver
quickest-silver13h ago
Sadly here I cannot revalidate context after setting the user state - this provider lives outside of router provider.
why is that?
rival-black
rival-blackOP3h ago
I cannot use useRouter hook in the provider as the router provider does not exist
No description
rival-black
rival-blackOP3h ago
I found this post - https://github.com/TanStack/router/discussions/2978 - where you instruct to use router from createRouter directly, not via the useRouter hook. Is it a solution?
GitHub
Warning: useRouter must be used inside a <RouterProvider> component...
I am creating an authenticated route using the TanStack Router documentation. According to the docs, I need to create an inner component like this: function InnerApp() { const auth = useAuth(); ret...
quickest-silver
quickest-silver3h ago
if you have a circular provider issue, then yes this helps sometimes you can also rearrange the provider hierachy
rival-black
rival-blackOP2h ago
Maybe InnerWrap property will be a better solution Or some effect to invalidate the router context when authContext change Ideally I wouldn't like the AuthProvider need to handle router context invalidation. I would prefer the router context to automatically detect state changes and revalidate This option also works:
import {
RouterProvider as TanstackRouterProvider,
createRouter,
useRouter,
} from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'
import {
TanstackQueryProvider,
queryClient,
} from './integrations/tanstack-query/tanstack-query-provider'
import { useAuth } from './context/auth-context'
import { SupabaseAuthProvider } from './integrations/supabase/supabase-auth-context'
import { ThemeProvider } from './integrations/shadcn/shadcn-theme-context'
import type { QueryClient } from '@tanstack/react-query'
import { useEffect } from 'react'

declare module '@tanstack/react-router' {
interface Register {
router: typeof router
}
}

export interface RouterContext {
queryClient: QueryClient
auth: {
isAuthenticated: boolean
}
}

const router = createRouter({
context: {
queryClient,
auth: undefined!,
},
InnerWrap: ({ children }) => {
const router = useRouter();
const { isAuthenticated } = useAuth();

useEffect(() => {
router.invalidate();
}, [isAuthenticated]);

return children;
},
routeTree,
defaultPreload: 'intent',
scrollRestoration: true,
defaultStructuralSharing: true,
defaultPreloadStaleTime: 0,
})

export function RouterProvider() {
const { isAuthenticated } = useAuth()

return (
<TanstackRouterProvider
router={router}
context={{
queryClient,
auth: {
isAuthenticated,
},
}}
/>
)
}
export function App() {
return (
<ThemeProvider defaultTheme="dark">
<TanstackQueryProvider>
<SupabaseAuthProvider>
<RouterProvider />
</SupabaseAuthProvider>
</TanstackQueryProvider>
</ThemeProvider>
)
}
import {
RouterProvider as TanstackRouterProvider,
createRouter,
useRouter,
} from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'
import {
TanstackQueryProvider,
queryClient,
} from './integrations/tanstack-query/tanstack-query-provider'
import { useAuth } from './context/auth-context'
import { SupabaseAuthProvider } from './integrations/supabase/supabase-auth-context'
import { ThemeProvider } from './integrations/shadcn/shadcn-theme-context'
import type { QueryClient } from '@tanstack/react-query'
import { useEffect } from 'react'

declare module '@tanstack/react-router' {
interface Register {
router: typeof router
}
}

export interface RouterContext {
queryClient: QueryClient
auth: {
isAuthenticated: boolean
}
}

const router = createRouter({
context: {
queryClient,
auth: undefined!,
},
InnerWrap: ({ children }) => {
const router = useRouter();
const { isAuthenticated } = useAuth();

useEffect(() => {
router.invalidate();
}, [isAuthenticated]);

return children;
},
routeTree,
defaultPreload: 'intent',
scrollRestoration: true,
defaultStructuralSharing: true,
defaultPreloadStaleTime: 0,
})

export function RouterProvider() {
const { isAuthenticated } = useAuth()

return (
<TanstackRouterProvider
router={router}
context={{
queryClient,
auth: {
isAuthenticated,
},
}}
/>
)
}
export function App() {
return (
<ThemeProvider defaultTheme="dark">
<TanstackQueryProvider>
<SupabaseAuthProvider>
<RouterProvider />
</SupabaseAuthProvider>
</TanstackQueryProvider>
</ThemeProvider>
)
}
And SupabaseAuthProvider is not dependent on router directly, which is cleaner.

Did you find this page helpful?