T
TanStack15h ago
deep-jade

I keep creating infinite loops when redirecting in beforeLoad

I think I have a fundamental misunderstanding of how the route loader work. In consequence, I already created multiple infinite redirect loops with tanstack router 🙁 Here's an instance where this happens. I navigate a user to the logout route:
export const Route = createFileRoute("/_form/logout")({
component: RouteComponent,
beforeLoad: async () => {
await authClient.signOut();
throw redirect({ to: "/login" });
},
});
export const Route = createFileRoute("/_form/logout")({
component: RouteComponent,
beforeLoad: async () => {
await authClient.signOut();
throw redirect({ to: "/login" });
},
});
The logout route redirects to /login but removed the session.
export const Route = createFileRoute("/_form/login")({
component: RouteComponent,
validateSearch: SearchSchema,
beforeLoad: async ({ search: { event }, context: { queryClient } }) => {
const sessionResult = await queryClient.fetchQuery(sessionQueryOptions());
if (sessionResult.data && sessionResult.data.session) {
throw redirect({ to: "/hello" });
}
},
});
export const Route = createFileRoute("/_form/login")({
component: RouteComponent,
validateSearch: SearchSchema,
beforeLoad: async ({ search: { event }, context: { queryClient } }) => {
const sessionResult = await queryClient.fetchQuery(sessionQueryOptions());
if (sessionResult.data && sessionResult.data.session) {
throw redirect({ to: "/hello" });
}
},
});
For some reason, the session is still active here (even though the cookie was removed). The `/hello route tries to get the session, does not see it and loops back to logout.
export const Route = createFileRoute("/hello")({
beforeLoad: async () => {
const sessionResult = await authClient.getSession();

const session = sessionResult.data?.session;

if (!session?.userId) {
throw redirect({
to: "/logout",
});
}
},
component: RouteComponent,
errorComponent: ({ error }) => {
return <div>{getErrorMessage(error)}</div>;
},
});
export const Route = createFileRoute("/hello")({
beforeLoad: async () => {
const sessionResult = await authClient.getSession();

const session = sessionResult.data?.session;

if (!session?.userId) {
throw redirect({
to: "/logout",
});
}
},
component: RouteComponent,
errorComponent: ({ error }) => {
return <div>{getErrorMessage(error)}</div>;
},
});
Now, I'd assume that a loop would be impossible since the session is removed. However, I see the url bar flickering. The flicker is so fast that I'm pretty sure nothing is waiting for async actions to resolve. I've been banging my head against this for a while now but it does not seem to make sense. Can anyone spot the issue here (if it's there)?
4 Replies
rival-black
rival-black14h ago
Quick question, why do you opt for queryClient for the login? It would make more sense to use the authClient instead? I'm wondering if maybe your sessionQueryOptions() has some weird caching rules since we shouldn't be caching authentication anyways
deep-jade
deep-jadeOP14h ago
I think this ia a really good point. Wasn't even aware I was doing this. I had it cached for an entire day:
export const sessionQueryOptions = () =>
queryOptions({
queryKey: ["user-sessions", "detail"],
queryFn: async () => {
const res = await authClient.getSession();
return res;
},
staleTime: 60 * 1000 * 60 * 24, // one day
});
export const sessionQueryOptions = () =>
queryOptions({
queryKey: ["user-sessions", "detail"],
queryFn: async () => {
const res = await authClient.getSession();
return res;
},
staleTime: 60 * 1000 * 60 * 24, // one day
});
rival-black
rival-black14h ago
Yeah probably just use authClient.getSession()
deep-jade
deep-jadeOP14h ago
Thanks, will check if this resolves it. Good catch!

Did you find this page helpful?