T
TanStack2y ago
like-gold

Issues with authenticated routes

Hi, trying out the TanStack Router. I'm experiencing issues with creating authenticated routes. It's a bit different from the approach using react-router-dom. The router has been set up correctly I believe, as it does work when I remove any routes related to authentication or layouts. What I have now:
const rootRoute = new RootRoute({ component: Root });

// Layout routes

const layoutRoute = new Route({
getParentRoute: () => rootRoute,
id: "layout",
component: Layout,
});

// Authenticated routes

const authenticatedRoutes = new Route({
getParentRoute: () => layoutRoute,
id: "auth-layout",
component: AuthComponent,
});

const dashboardRoute = new Route({
getParentRoute: () => authenticatedRoutes,
path: "/dashboard",
component: Dashboard,
});

// Unauthenticated routes

const homeRoute = new Route({
getParentRoute: () => rootRoute,
path: "/",
component: Home,
});

const loginRoute = new Route({
getParentRoute: () => rootRoute,
path: "/login",
component: Login,
});

const routeTree = rootRoute.addChildren([
homeRoute,
loginRoute,
authenticatedRoutes.addChildren([dashboardRoute]),
]);

export { routeTree };
const rootRoute = new RootRoute({ component: Root });

// Layout routes

const layoutRoute = new Route({
getParentRoute: () => rootRoute,
id: "layout",
component: Layout,
});

// Authenticated routes

const authenticatedRoutes = new Route({
getParentRoute: () => layoutRoute,
id: "auth-layout",
component: AuthComponent,
});

const dashboardRoute = new Route({
getParentRoute: () => authenticatedRoutes,
path: "/dashboard",
component: Dashboard,
});

// Unauthenticated routes

const homeRoute = new Route({
getParentRoute: () => rootRoute,
path: "/",
component: Home,
});

const loginRoute = new Route({
getParentRoute: () => rootRoute,
path: "/login",
component: Login,
});

const routeTree = rootRoute.addChildren([
homeRoute,
loginRoute,
authenticatedRoutes.addChildren([dashboardRoute]),
]);

export { routeTree };
root.tsx
export const Root = () => (
<Outlet />
);
export const Root = () => (
<Outlet />
);
layout.tsx
export const Layout = () => (
<Navigation>
<Outlet />
</Navigation>
);
export const Layout = () => (
<Navigation>
<Outlet />
</Navigation>
);
authComponent.tsx
export const AuthComponent = () => {
const { authenticated } = useAuth();

return (
authenticated ? <Outlet /> : <Navigate to="/login" />
);
};
export const AuthComponent = () => {
const { authenticated } = useAuth();

return (
authenticated ? <Outlet /> : <Navigate to="/login" />
);
};
The dashboard and home components only contain some text. I can access the home and login page, however I cannot access /dashboard. Trying to access it will throw an error: Uncaught TypeError: Cannot read properties of undefined (reading 'options') Does anyone know what I've done wrong setting this up?
21 Replies
wise-white
wise-white2y ago
This could be a regression, so let me debug as soon as I can crawl out of bed 😝
like-gold
like-goldOP2y ago
Thank you! I'm working on it later too, maybe I will find it Also if you have a recommended way of handling this flow I'd gladly hear it. Currently checking this out because it seems handy but we don't have a lot of time to refactor previous made contexts/providers @Tanner Linsley I got a fix with a test provider/context which could simulate our current project. Thanks to Potato Science in #router This seems to work:
// App.tsx
const TanStackRouterProvider = () => {
const auth = useAuth();

return <RouterProvider router={router} context={{ auth }} />;
};

export const App = () => (
<QueryClientProvider client={queryClient}>
<AuthProvider>
<Provider store={store}>
<TanStackRouterProvider />
</Provider>
</AuthProvider>

<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
// App.tsx
const TanStackRouterProvider = () => {
const auth = useAuth();

return <RouterProvider router={router} context={{ auth }} />;
};

export const App = () => (
<QueryClientProvider client={queryClient}>
<AuthProvider>
<Provider store={store}>
<TanStackRouterProvider />
</Provider>
</AuthProvider>

<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
);
// routes.ts
const rootRoute = rootRouteWithContext<{
auth: AuthContext
}>()({ component: Root });

// Layout routes

const layoutRoute = new Route({
getParentRoute: () => rootRoute,
id: "layout",
component: Layout,
});

// Authenticated routes

const authenticatedRoutes = new Route({
getParentRoute: () => rootRoute,
id: "auth-layout",
beforeLoad: ({ context, location }) => {
if (!context.auth.authenticated){
throw redirect({
to: "/login",
search: { redirect: location.href },
});
}
},
});

const dashboardRoute = new Route({
getParentRoute: () => authenticatedRoutes,
path: "/dashboard",
component: Dashboard,
});

// Unauthenticated routes

const homeRoute = new Route({
getParentRoute: () => rootRoute,
path: "/",
component: Home,
});

const loginRoute = new Route({
getParentRoute: () => rootRoute,
path: "/login",
component: Login,
});

const routeTree = rootRoute.addChildren([
homeRoute,
loginRoute,
authenticatedRoutes.addChildren([dashboardRoute]),
]);

export { routeTree };
// routes.ts
const rootRoute = rootRouteWithContext<{
auth: AuthContext
}>()({ component: Root });

// Layout routes

const layoutRoute = new Route({
getParentRoute: () => rootRoute,
id: "layout",
component: Layout,
});

// Authenticated routes

const authenticatedRoutes = new Route({
getParentRoute: () => rootRoute,
id: "auth-layout",
beforeLoad: ({ context, location }) => {
if (!context.auth.authenticated){
throw redirect({
to: "/login",
search: { redirect: location.href },
});
}
},
});

const dashboardRoute = new Route({
getParentRoute: () => authenticatedRoutes,
path: "/dashboard",
component: Dashboard,
});

// Unauthenticated routes

const homeRoute = new Route({
getParentRoute: () => rootRoute,
path: "/",
component: Home,
});

const loginRoute = new Route({
getParentRoute: () => rootRoute,
path: "/login",
component: Login,
});

const routeTree = rootRoute.addChildren([
homeRoute,
loginRoute,
authenticatedRoutes.addChildren([dashboardRoute]),
]);

export { routeTree };
wise-white
wise-white2y ago
Yes! This should work 🙂 Keep in mind, RouterProvider supports both a Wrap and InnerWrap component that will do just like they say. Just remember to render the children prop 🙂 But this works just fine
like-gold
like-goldOP2y ago
I see, I'll look into that as well
wise-white
wise-white2y ago
<Options.Wrap>
<RouterProvider>
<Options.InnerWrap>
<Matches>
</Options.InnerWrap>
</RouterProvider>
</Options.Wrap>
<Options.Wrap>
<RouterProvider>
<Options.InnerWrap>
<Matches>
</Options.InnerWrap>
</RouterProvider>
</Options.Wrap>
So you can do
<RouterProvider
Wrap={({ children }) =>
<MyProvider>{children}</MyProvider>
}
/>
<RouterProvider
Wrap={({ children }) =>
<MyProvider>{children}</MyProvider>
}
/>
like-gold
like-goldOP2y ago
I'm not entirely sure what this wrap is though Oh like that, I see
wise-white
wise-white2y ago
Or...
like-gold
like-goldOP2y ago
For how long has TanStack router been out for? It's been awesome to work with so far, very well documented
wise-white
wise-white2y ago
<RouterProvider
Wrap={({ children }) =>
<MyProvider>{children}</MyProvider>
}
InnerWrap={({ children }) => {
// You have access to router context here
const routerState = useRouterState()

return children
}}
/>
<RouterProvider
Wrap={({ children }) =>
<MyProvider>{children}</MyProvider>
}
InnerWrap={({ children }) => {
// You have access to router context here
const routerState = useRouterState()

return children
}}
/>
It's not out yet lol It's been in beta for around 16 months alpha for 12 months before that I've been working on it for a total of ~3 years
like-gold
like-goldOP2y ago
That's quite some time! Do you have a roadmap or such before you make the v1 release?
wise-white
wise-white2y ago
I'm finalizing docs now So asap
like-gold
like-goldOP2y ago
I'm looking forward to it! It's been great working with it honestly. A bit more code, sure, but the benefits are great. I actually came across router and query because of a talk you did
wise-white
wise-white2y ago
Oh yeah? Which one?
like-gold
like-goldOP2y ago
Checked it out, and I absolutely love how easy it is to use react query One second
like-gold
like-goldOP2y ago
ReactJS Utah
YouTube
Intro to TanStack Router with Tanner Linsley
Tanner Linsley gives us a breakdown and introduction to the new TanStack Router, a fully typesafe router with 1st class search params. ReactJS Utah Meetup: Nov 2022
like-gold
like-goldOP2y ago
And now i'm checking the router out, and ye it's a bit of a switch from react router dom but it seems really easy to use as well
wise-white
wise-white2y ago
Well thanks! People are likely to get different vibes from TSR, and that's good React Router isn't really designed for full type safety, and thus, it can feel "simpler" at times.
like-gold
like-goldOP2y ago
I'm looking at the layouts now, which seems quite easy to implement for different layouts too
wise-white
wise-white2y ago
But that level of simplicity cannot support a strictly typed system. Yeah, just use an id instead of a path
like-gold
like-goldOP2y ago
I'm currently doing my final semester for university at my current employer. And I think next meeting I'll bring both query and router up to switch too for multiple projects Yeah I saw that Definitely! Well thank you for creating tanstack query and router, and your help. It's definitely going to be a lot of value for my final assignment!
wise-white
wise-white2y ago
Thanks! My pleasure.

Did you find this page helpful?