T
TanStack2y ago
xenial-black

Context is not up to date after navigate

Hey guys, I'm trying to handle redirection after login in. For now, I got my Login Component that is working fine, it updates the AuthContext so I can use my user globally. I would like to be able to redirect my user to /dashboard after the successful login. My /dashboard route is a protected route, I can access it only if context.useris not null. But when I'm logging in and trying to redirect, the context is not yet up to date IN THE DASHBOARD ROUTE, so I got redirect back to the login page. Do anyone have an idea on how to solve this ?
3 Replies
xenial-black
xenial-blackOP2y ago
Login.tsx
import { login } from '#/api/mutations/auth'
import { AuthContext } from '#/context/AuthProvider'
import { getError, hasError } from '#/utils/errors'
import { Button } from '#components/catalyst/button'
import { ErrorMessage, Field, Label } from '#components/catalyst/fieldset'
import { Input } from '#components/catalyst/input'
import { ApiError } from '#types/error'
import { AuthContextType, LoginPayload, User } from '#types/user'
import { useForm } from '@tanstack/react-form'
import { useMutation } from '@tanstack/react-query'
import { createFileRoute, redirect, useNavigate } from '@tanstack/react-router'
import { enqueueSnackbar } from 'notistack'
import { useContext } from 'react'

export const Route = createFileRoute('/login')({
beforeLoad: ({ context }) => {
if (context.user) {
throw redirect({
to: '/dashboard',
})
}
},
component: () => <Login />,
})

const Login = () => {
const { updateUser } = useContext(AuthContext) as AuthContextType
const navigate = useNavigate({ from: '/login' })

const { mutate, isPending, error } = useMutation<
User,
ApiError[],
LoginPayload
>({
mutationFn: (payload: LoginPayload) => {
return login(payload)
},
onSuccess: (data) => {
updateUser(data)
enqueueSnackbar('Vous êtes connecté', { variant: 'success' })
},
onSettled: async () => {
await navigate({ to: '/dashboard' })
},
})

const form = useForm({
defaultValues: {
email: '',
password: '',
},
onSubmit: async ({ value }) => {
mutate(value)
},
})

return (
// THE FORM HERE
)
}
import { login } from '#/api/mutations/auth'
import { AuthContext } from '#/context/AuthProvider'
import { getError, hasError } from '#/utils/errors'
import { Button } from '#components/catalyst/button'
import { ErrorMessage, Field, Label } from '#components/catalyst/fieldset'
import { Input } from '#components/catalyst/input'
import { ApiError } from '#types/error'
import { AuthContextType, LoginPayload, User } from '#types/user'
import { useForm } from '@tanstack/react-form'
import { useMutation } from '@tanstack/react-query'
import { createFileRoute, redirect, useNavigate } from '@tanstack/react-router'
import { enqueueSnackbar } from 'notistack'
import { useContext } from 'react'

export const Route = createFileRoute('/login')({
beforeLoad: ({ context }) => {
if (context.user) {
throw redirect({
to: '/dashboard',
})
}
},
component: () => <Login />,
})

const Login = () => {
const { updateUser } = useContext(AuthContext) as AuthContextType
const navigate = useNavigate({ from: '/login' })

const { mutate, isPending, error } = useMutation<
User,
ApiError[],
LoginPayload
>({
mutationFn: (payload: LoginPayload) => {
return login(payload)
},
onSuccess: (data) => {
updateUser(data)
enqueueSnackbar('Vous êtes connecté', { variant: 'success' })
},
onSettled: async () => {
await navigate({ to: '/dashboard' })
},
})

const form = useForm({
defaultValues: {
email: '',
password: '',
},
onSubmit: async ({ value }) => {
mutate(value)
},
})

return (
// THE FORM HERE
)
}
Dashboard.tsx
import { createFileRoute, redirect } from '@tanstack/react-router'

export const Route = createFileRoute('/dashboard')({
beforeLoad: ({ context, location }) => {
console.log(context)
if (!context.user) {
console.log('redirect')
throw redirect({
to: '/login',
search: {
redirect: location.href,
},
})
}
},
component: () => <div>Hello /dashboard!</div>,
})
import { createFileRoute, redirect } from '@tanstack/react-router'

export const Route = createFileRoute('/dashboard')({
beforeLoad: ({ context, location }) => {
console.log(context)
if (!context.user) {
console.log('redirect')
throw redirect({
to: '/login',
search: {
redirect: location.href,
},
})
}
},
component: () => <div>Hello /dashboard!</div>,
})
May be there is something I don't know about context update and update in Tanstack, I'm pretty new to all of this ! Thanks!
exotic-emerald
exotic-emerald2y ago
where does your route context is updated ?
xenial-black
xenial-blackOP2y ago
it is update through the updateUser function in the onSuccess method from useMutation The context is given to the route through the routerProvider
import { queryClient } from '#/api/queryClient'
import { AuthContext } from '#/context/AuthProvider'
import { routeTree } from '#/routeTree.gen'
import { RouterProvider, createRouter } from '@tanstack/react-router'
import { useContext } from 'react'
import { AuthContextType } from './types/user'

const router = createRouter({
routeTree,
context: {
queryClient: undefined!,
user: undefined!,
updateUser: () => {},
logout: () => {},
},
defaultPreload: 'intent',
defaultPreloadStaleTime: 0,
})

export default function App() {
const authContext = useContext(AuthContext) as AuthContextType
return (
<RouterProvider router={router} context={{ ...authContext, queryClient }} />
)
}
import { queryClient } from '#/api/queryClient'
import { AuthContext } from '#/context/AuthProvider'
import { routeTree } from '#/routeTree.gen'
import { RouterProvider, createRouter } from '@tanstack/react-router'
import { useContext } from 'react'
import { AuthContextType } from './types/user'

const router = createRouter({
routeTree,
context: {
queryClient: undefined!,
user: undefined!,
updateUser: () => {},
logout: () => {},
},
defaultPreload: 'intent',
defaultPreloadStaleTime: 0,
})

export default function App() {
const authContext = useContext(AuthContext) as AuthContextType
return (
<RouterProvider router={router} context={{ ...authContext, queryClient }} />
)
}
With this I can access the context directly in the route declaration. This is why I can access the context property on the beforeLoad method I tried another way to redirect after login by using router.history.push('/dashboard') But I'm still on the same point, I got redirected to login because the context is not up to date :/

Did you find this page helpful?