T
TanStack2y ago
ambitious-aqua

Router context and beforeLoad race condition

App.tsx
import { RouterProvider, createRouter } from "@tanstack/react-router";
import { routeTree } from "@/routeTree.gen";
import { useAuthState } from "react-firebase-hooks/auth";
import { auth } from "config";

declare module "@tanstack/react-router" {
interface Register {
router: typeof router;
}
}
const router = createRouter({
routeTree,
context: { user: undefined!, loading: true, error: undefined }
});

export default function App() {
const [user, loading, error] = useAuthState(auth);

return <RouterProvider router={router} context={{ user, loading, error }} />;
}
import { RouterProvider, createRouter } from "@tanstack/react-router";
import { routeTree } from "@/routeTree.gen";
import { useAuthState } from "react-firebase-hooks/auth";
import { auth } from "config";

declare module "@tanstack/react-router" {
interface Register {
router: typeof router;
}
}
const router = createRouter({
routeTree,
context: { user: undefined!, loading: true, error: undefined }
});

export default function App() {
const [user, loading, error] = useAuthState(auth);

return <RouterProvider router={router} context={{ user, loading, error }} />;
}
_auth.tsx
import { Outlet, createFileRoute, redirect } from "@tanstack/react-router";
import { auth } from "config"; // This is a import alis that points to my firebaseConfig.ts

export const Route = createFileRoute("/_auth")({
beforeLoad: async ({ context, location }) => {
await auth.authStateReady();
await new Promise(resolve => setTimeout(resolve, 10));

if (context.user === null) {
throw redirect({
to: "/login",
search: {
redirect: location.href
}
});
}
},
component: () => <Outlet />
});
import { Outlet, createFileRoute, redirect } from "@tanstack/react-router";
import { auth } from "config"; // This is a import alis that points to my firebaseConfig.ts

export const Route = createFileRoute("/_auth")({
beforeLoad: async ({ context, location }) => {
await auth.authStateReady();
await new Promise(resolve => setTimeout(resolve, 10));

if (context.user === null) {
throw redirect({
to: "/login",
search: {
redirect: location.href
}
});
}
},
component: () => <Outlet />
});
This currently works but I think the delay may be unnecessary and there may be a more elegant way of doing this. I also tried creating the router inside the App.tsx like so:
export default function App() {
const [user, loading, error] = useAuthState(auth);

const router = createRouter({
routeTree,
context: { user: undefined, loading: true, error: undefined }
});

return <RouterProvider router={router} context={{ user, loading, error }} />;
}
export default function App() {
const [user, loading, error] = useAuthState(auth);

const router = createRouter({
routeTree,
context: { user: undefined, loading: true, error: undefined }
});

return <RouterProvider router={router} context={{ user, loading, error }} />;
}
And it worked fine but didn't let me declare the module since it can only be done at the top level of a file. An ambient module declaration is only allowed at the top level in a file. It would also likely hurt performance.
29 Replies
ambitious-aqua
ambitious-aquaOP2y ago
I didn't know how to explain the question concisely so I'm open to suggestions @Sean Cassiere could you give me a hand? Also flushSync didn't work
optimistic-gold
optimistic-gold2y ago
I will need a Stackblitz to work with. Trying to debug, over text is not great.
ambitious-aqua
ambitious-aquaOP2y ago
Alright, just give me a minute.
optimistic-gold
optimistic-gold2y ago
Its 3am here in new zealand. Post the link here, I'll check it out once I'm up.
ambitious-aqua
ambitious-aquaOP2y ago
👍 https://stackblitz.com/~/github.com/MatteoPrendi/STS-Club Christ this was more difficult than I thought
optimistic-gold
optimistic-gold2y ago
Any valid test credentials I could use for this? DM them to me please.
optimistic-gold
optimistic-gold2y ago
I made a couple changes off the bat and I'm testing it out. Seems to be working. https://stackblitz.com/edit/github-rsii7p?file=src%2Froutes%2F_auth.tsx
Sean Cassiere
StackBlitz
router - Matteoprendi - Sts Club - StackBlitz
Run Sts Club created by Matteo Prendi on StackBlitz
optimistic-gold
optimistic-gold2y ago
Nothing fundamental though was changed. Let me know if I missed the actual problem.
ambitious-aqua
ambitious-aquaOP2y ago
It doesn't automatically redirect you when you're logged in like when you refresh the page it should take you to home since the user is persisted I don't know if I explained myself correctly
optimistic-gold
optimistic-gold2y ago
Let me check
optimistic-gold
optimistic-gold2y ago
Am I missing something in my testing strat?
optimistic-gold
optimistic-gold2y ago
You mean when you login, the user session is persisted?
ambitious-aqua
ambitious-aquaOP2y ago
After logging in try refreshing the page and open the devtools there should be a user object in the context which means the user is logged in let me make a video Here it is in 60 fps cuz 30 is just unwatchable
ambitious-aqua
ambitious-aquaOP2y ago
ambitious-aqua
ambitious-aquaOP2y ago
I also think that since there's a bit of a delay when updating the context you need to hit the sign in button twicefor it to correctly redirect you to the home page
optimistic-gold
optimistic-gold2y ago
Yup, checking it out. Something about this firebase thing doesn't seem to be the most reactive thing in the world. WIll hack at it for a bit more to try and figure this out
ambitious-aqua
ambitious-aquaOP2y ago
Yea now that you mention it, it might the asynchronous nature of the login function Well if it's not the routers fault I'll just keep the 20 ms delay
optimistic-gold
optimistic-gold2y ago
I fixed the user persistance problem. Its the reactivity that bugging me.
optimistic-gold
optimistic-gold2y ago
See the App.tsx, that fixed the persistence problem. https://stackblitz.com/edit/github-rsii7p?file=src%2FApp.tsx
Sean Cassiere
StackBlitz
router - Matteoprendi - Sts Club - StackBlitz
Run Sts Club created by Matteo Prendi on StackBlitz
ambitious-aqua
ambitious-aquaOP2y ago
Yea I saw that I feel so stupid right now
optimistic-gold
optimistic-gold2y ago
There was a delay between the response received from firebase and it actually being reactively sent back to React-land. For this it looks like it needed that delay trick. Other than that everything looks good. See the console logs for this delay btw. Just comment out the delay in the login page and see the chaos ensue.
ambitious-aqua
ambitious-aquaOP2y ago
Works like a charm thanks a bunch, you were incredibly helpful
optimistic-gold
optimistic-gold2y ago
Np Have a good weekend !
ambitious-aqua
ambitious-aquaOP2y ago
you too I had to delete the old repo and create a new one because the company I was building the website for demanded it I will make a detailed explaination of what changed and what happened tomorrow App.tsx
const router = createRouter({
routeTree,
context: { user: undefined, loading: true, error: undefined }
});

export default function App() {
const [user, loading, error] = useAuthState(auth);

return (
<ThemeProvider>
{loading ? (
<main className="bg-background text-foreground dark">Sta caricando...</main>
) : (
<RouterProvider router={router} context={{ user, loading, error }} />
)}
</ThemeProvider>
);
}
const router = createRouter({
routeTree,
context: { user: undefined, loading: true, error: undefined }
});

export default function App() {
const [user, loading, error] = useAuthState(auth);

return (
<ThemeProvider>
{loading ? (
<main className="bg-background text-foreground dark">Sta caricando...</main>
) : (
<RouterProvider router={router} context={{ user, loading, error }} />
)}
</ThemeProvider>
);
}
__root.tsx
interface ContextTypes {
user: User | null | undefined;
loading: boolean;
error: Error | undefined;
}
export const Route = createRootRouteWithContext<ContextTypes>()({
beforeLoad: async () => {
await auth.authStateReady();
},
component: () => <Layout />
});
interface ContextTypes {
user: User | null | undefined;
loading: boolean;
error: Error | undefined;
}
export const Route = createRootRouteWithContext<ContextTypes>()({
beforeLoad: async () => {
await auth.authStateReady();
},
component: () => <Layout />
});
_auth.tsx
export const Route = createFileRoute("/_auth")({
beforeLoad: async ({ context, location }) => {
if (!context.user) {
throw redirect({
to: "/login",
search: { redirect: location.href }
});
}
},
component: () => <Layout />
});
export const Route = createFileRoute("/_auth")({
beforeLoad: async ({ context, location }) => {
if (!context.user) {
throw redirect({
to: "/login",
search: { redirect: location.href }
});
}
},
component: () => <Layout />
});
Basically we wait until the authState is ready on the root route so that by the time the beforeLoad of the _auth route is run, the context is updated.
ambitious-aqua
ambitious-aquaOP2y ago
GitHub
GitHub - MatteoPrendi/STS-intranet
Contribute to MatteoPrendi/STS-intranet development by creating an account on GitHub.
robust-apricot
robust-apricot15mo ago
Hi I believe I am facing the similar issue I have AuthProvider and the context values(isAuthenticated, userinfo etc) are updated as soon as Login api is sucess but the the context auth that is passed to RouterProvider is still having initial values(isAuthenticated:false) I could not find the fix as there is issue in the above URLs. I can see in the video of @buttermint that the navigation was working when the login button is clicked on second time, I am facing with the same issue. when the Login button is first time clicked the auth context is updating but redirect wont happen but happens on second click..
harsh-harlequin
harsh-harlequin15mo ago
Same problem after updating router version 1.2.x to 1.3.x
robust-apricot
robust-apricot15mo ago
I am having "@tanstack/react-router": "^1.53.1",
robust-apricot
robust-apricot15mo ago
after re-reading this message, finally I was able to fix the issue by adding some timeOut after login so the sleep method in the example docs is for real🙂
No description

Did you find this page helpful?