TanStackT
TanStack2y ago
53 replies
ordinary-sapphire

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 }} />;
}


_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 />
});


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 }} />;
}

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.
Was this page helpful?