T
TanStack•2y ago
vicious-gold

Context & protected routes

Hi everyone, i'm having some issues with protected routes: If I go to a route from a link, the context is still passed, and there is not issues. But if I go to the URL from my browser like localhost/account then the context is null for like 0.2s. But using this condition in my Route:
beforeLoad: ({ context, location }) => {
if (!context.auth.isAuthenticated) {

console.log("redirecting to /")
console.log(context.auth)

throw redirect({
to: '/',
search: {
redirect: location.href,
},
})
}
},
beforeLoad: ({ context, location }) => {
if (!context.auth.isAuthenticated) {

console.log("redirecting to /")
console.log(context.auth)

throw redirect({
to: '/',
search: {
redirect: location.href,
},
})
}
},
It redirect the user to the main page. Any ways to avoid that?
21 Replies
vicious-gold
vicious-goldOP•2y ago
maybe @Sean Cassiere aka my saviour?
genetic-orange
genetic-orange•2y ago
Hmmm honestly not too sure. It could have something to do with your Auth set up maybe? How is auth handled? A package? or something custom?
vicious-gold
vicious-goldOP•2y ago
nop, with a context like we saw in #__root and context any ways to avoid that please? (up) main.tsx
import ReactDOM from 'react-dom/client'
import './index.css'

import {createRouter, ErrorComponent, RouterProvider,} from '@tanstack/react-router'
import {routeTree} from "@/routeTree.gen.ts";
import {AuthContext, AuthProvider, useAuth} from "@/auth.tsx";
import {QueryClient, QueryClientProvider} from "@tanstack/react-query";


export interface RouterContext {
auth: AuthContext
queryClient: QueryClient
}


const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: Infinity,
},
},
})

const router = createRouter({
routeTree,
defaultPendingComponent: () => (
<div className={`p-2 text-2xl`}>
todo: spinner loading
</div>
),

defaultErrorComponent: ({error}) => <ErrorComponent error={error}/>,
context: {
auth: undefined!, // We'll inject this when we render
queryClient: queryClient
},
defaultPreload: 'intent',
})

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

function InnerApp() {

const auth = useAuth();
return <RouterProvider router={router} context={{auth}}/>
}

function App() {
return (
<QueryClientProvider client={queryClient}>
<AuthProvider>
<InnerApp/>
</AuthProvider>
</QueryClientProvider>

)
}

const rootElement = document.getElementById('app')!

if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement)
root.render(<App/>)
}
import ReactDOM from 'react-dom/client'
import './index.css'

import {createRouter, ErrorComponent, RouterProvider,} from '@tanstack/react-router'
import {routeTree} from "@/routeTree.gen.ts";
import {AuthContext, AuthProvider, useAuth} from "@/auth.tsx";
import {QueryClient, QueryClientProvider} from "@tanstack/react-query";


export interface RouterContext {
auth: AuthContext
queryClient: QueryClient
}


const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: Infinity,
},
},
})

const router = createRouter({
routeTree,
defaultPendingComponent: () => (
<div className={`p-2 text-2xl`}>
todo: spinner loading
</div>
),

defaultErrorComponent: ({error}) => <ErrorComponent error={error}/>,
context: {
auth: undefined!, // We'll inject this when we render
queryClient: queryClient
},
defaultPreload: 'intent',
})

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

function InnerApp() {

const auth = useAuth();
return <RouterProvider router={router} context={{auth}}/>
}

function App() {
return (
<QueryClientProvider client={queryClient}>
<AuthProvider>
<InnerApp/>
</AuthProvider>
</QueryClientProvider>

)
}

const rootElement = document.getElementById('app')!

if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement)
root.render(<App/>)
}
AuthProvider
import * as React from 'react'
import {User} from "@/types/user_type.ts";
import {useSession} from "@/hooks/use_session.tsx";


export interface AuthContext {
isAuthenticated: boolean
user: User | null
}

const AuthContext = React.createContext<AuthContext | null>(null)

export function AuthProvider({ children }: { children: React.ReactNode }) {
let user = null

const session = useSession();

if (session.data) {
user = session.data
}

const isAuthenticated = !!user

return (
<AuthContext.Provider value={{ isAuthenticated, user }}>
{children}
</AuthContext.Provider>
)
}


export function useAuth() {
const context = React.useContext(AuthContext)

if (!context) {
throw new Error('useAuth must be used within an AuthProvider')
}
return context
}
import * as React from 'react'
import {User} from "@/types/user_type.ts";
import {useSession} from "@/hooks/use_session.tsx";


export interface AuthContext {
isAuthenticated: boolean
user: User | null
}

const AuthContext = React.createContext<AuthContext | null>(null)

export function AuthProvider({ children }: { children: React.ReactNode }) {
let user = null

const session = useSession();

if (session.data) {
user = session.data
}

const isAuthenticated = !!user

return (
<AuthContext.Provider value={{ isAuthenticated, user }}>
{children}
</AuthContext.Provider>
)
}


export function useAuth() {
const context = React.useContext(AuthContext)

if (!context) {
throw new Error('useAuth must be used within an AuthProvider')
}
return context
}
genetic-orange
genetic-orange•2y ago
Been looking through this, and this is quite weird. Its definitely a problem in the Context area (not the router). Will check on this later again.
vicious-gold
vicious-goldOP•2y ago
thanks !
vicious-gold
vicious-goldOP•2y ago
I've found a reason of why this is not working:
No description
vicious-gold
vicious-goldOP•2y ago
No description
optimistic-gold
optimistic-gold•2y ago
if you use useSuspenseQuery instead of useQuery , session.datawill never be undefined
vicious-gold
vicious-goldOP•2y ago
okay, but in case of an exception, like 401 if the user is not authenticated, how can I handle that?
optimistic-gold
optimistic-gold•2y ago
then you need to add an error boundary
optimistic-gold
optimistic-gold•2y ago
Suspense | TanStack Query Docs
React Query can also be used with React's Suspense for Data Fetching API's. For this, we have dedicated hooks: useSuspenseQuery
vicious-gold
vicious-goldOP•2y ago
okay 🙂 But, if for example, i'm on a route that don't need auth, it will spread an error for 'nothing'
optimistic-gold
optimistic-gold•2y ago
then don't put the auth provider in the router context because then you will use it on every route
vicious-gold
vicious-goldOP•2y ago
so, I should create multiple router ?
optimistic-gold
optimistic-gold•2y ago
no, but use route context instead in routes that require auth also check this thread https://discord.com/channels/719702312431386674/1204174457002790953 and the example repo that @Sean Cassiere linked to
vicious-gold
vicious-goldOP•2y ago
mhh, i'll need to rework this so it'll not be that easy so in this example, I should create a 'public' layout then a 'authenticated' layout?
optimistic-gold
optimistic-gold•2y ago
it's one way to separate the auth handling logic for sure. but it really depends on your app structure if you can cleanly separate between a public branch and a authed-branch in your routetree, then do it
vicious-gold
vicious-goldOP•2y ago
actually it's an e-commerce website, so basic things like: -basket -auth -order ...
optimistic-gold
optimistic-gold•2y ago
so can a basket be viewed without being logged in?
vicious-gold
vicious-goldOP•2y ago
basket, order, account should be protected and others routes like / or /auth should be public so yeah it can be a solution this is what Sean did on his project
vicious-gold
vicious-goldOP•2y ago
GitHub
nv-rental-clone/src/routes/__root.tsx at master · SeanCassiere/nv-r...
Navotar with Tailwind and the Tanstack. Contribute to SeanCassiere/nv-rental-clone development by creating an account on GitHub.

Did you find this page helpful?