Replies: 5 comments 16 replies
-
Facing the same issue, i also see the authenticated route example provided in documentation , login.tsx import * as React from 'react'
import {
createFileRoute,
redirect,
useRouter,
useRouterState,
} from '@tanstack/react-router'
import { z } from 'zod'
import { useAuth } from '../auth'
import { sleep } from '../utils'
const fallback = '/dashboard' as const
export const Route = createFileRoute('/login')({
validateSearch: z.object({
redirect: z.string().optional().catch(''),
}),
beforeLoad: ({ context, search }) => {
if (context.auth.isAuthenticated) {
throw redirect({ to: search.redirect || fallback })
}
},
component: LoginComponent,
})
function LoginComponent() {
const auth = useAuth()
const router = useRouter()
const isLoading = useRouterState({ select: (s) => s.isLoading })
const navigate = Route.useNavigate()
const [isSubmitting, setIsSubmitting] = React.useState(false)
const search = Route.useSearch()
const onFormSubmit = async (evt: React.FormEvent<HTMLFormElement>) => {
setIsSubmitting(true)
try {
evt.preventDefault()
const data = new FormData(evt.currentTarget)
const fieldValue = data.get('username')
if (!fieldValue) return
const username = fieldValue.toString()
await auth.login(username)
await router.invalidate()
// This is just a hack being used to wait for the auth state to update
// in a real app, you'd want to use a more robust solution
await sleep(1)
await navigate({ to: search.redirect || fallback })
} catch (error) {
console.error('Error logging in: ', error)
} finally {
setIsSubmitting(false)
}
}
const isLoggingIn = isLoading || isSubmitting
return (
<div className="p-2 grid gap-2 place-items-center">
<h3 className="text-xl">Login page</h3>
{search.redirect ? (
<p className="text-red-500">You need to login to access this page.</p>
) : (
<p>Login to see all the cool content in here.</p>
)}
<form className="mt-4 max-w-lg" onSubmit={onFormSubmit}>
<fieldset disabled={isLoggingIn} className="w-full grid gap-2">
<div className="grid gap-2 items-center min-w-[300px]">
<label htmlFor="username-input" className="text-sm font-medium">
Username
</label>
<input
id="username-input"
name="username"
placeholder="Enter your name"
type="text"
className="border border-gray-300 rounded-md p-2 w-full"
required
/>
</div>
<button
type="submit"
className="bg-blue-500 text-white py-2 px-4 rounded-md w-full disabled:bg-gray-300 disabled:text-gray-500"
>
{isLoggingIn ? 'Loading...' : 'Login'}
</button>
</fieldset>
</form>
</div>
)
} There is a comment mention that need to implement a robust way for waiting the state update, but i am not sure how to do that and how it consider a robust way. I try below and not work useEffect(() => {
if (isAuthenticated) {
navigate({ to: "/somepath" });
}
}, [isAuthenticated]); |
Beta Was this translation helpful? Give feedback.
-
There was also a problem with async user status checking. If possible, please give more examples on how to deal with such cases |
Beta Was this translation helpful? Give feedback.
-
I would love to see this 'more robust' solution without having to add any other dependencies haha, facing the same problem here |
Beta Was this translation helpful? Give feedback.
-
I'm facing the same issue as well. Even when a firebase user is logged in it's momentarily async to load the user into state and by then the before load has already fired and redirected user to |
Beta Was this translation helpful? Give feedback.
-
I chose to wrap my Auth instance in a promise that resolves when it is done loading: interface RouterContext {
auth: Promise<AuthInterface>,
...
} // main.tsx
...
let resolveAuthClient: (client: AuthInterface) => void;
export const authClient: Promise<AuthInterface> = new Promise(
(resolve) => { resolveAuthClient = resolve }
);
function InnerApp() {
const auth = useAuth();
useEffect(() => {
if (auth.isLoading) return;
resolveAuthClient(auth);
}, [auth, auth.isLoading]);
return <RouterProvider router={router} context={{ auth: authClient }} />;
} // _auth.tsx
export const Route = createFileRoute('/_auth')({
pendingComponent: Spinner,
async beforeLoad({ context }) {
const auth = await context.auth; // Wait for auth to be done loading
// Do your stuff here
},
component: () => <Outlet />,
}); Then any route under the folder EDIT: Cutting edge ES2024 edition: const authClient = new Promise.withResolvers<AuthInterface>()
function InnerApp() {
const auth = useAuth();
useEffect(() => {
if (auth.isLoading) return;
authClient.resolve(auth);
}, [auth, auth.isLoading]);
return <RouterProvider router={router} context={{ auth: authClient }} />;
} |
Beta Was this translation helpful? Give feedback.
-
Hi,
I have tried to follow the example of a protected route with modification for checking the user status from the server asynchronously, but I wasn't successful because
beforeLoad
will return before the context state changes. How can I fix this problem?auth.tsx:
index.tsx:
Beta Was this translation helpful? Give feedback.
All reactions