Pass authed user data to protected routes

I am trying to pass validated data to protected routes so I don't have to check undefined. Routes are currently protected with RouteGuard which redirects to the sign in page if there is no user. If there is a user, I want to pass the validated data to all nested routes without having to manually pass the data to each route or setup a context provider for each route.
const RouteGuard: ParentComponent = (props) => {
const user = true; //Fetch User
const navigate = useNavigate();
if (!user) {
navigate("/auth/sign-in");
}
return (
<>{props.children}</>
);
};

const AuthWrapper: ParentComponent = (props) => (
<AuthProvider>{props.children}</AuthProvider>
);

const App: Component = () => {
return (
<Router>
<Route path="/auth" component={AuthLayout}>
<Route path="sign-in" component={SignInUp} />
</Route>
<Route path="/" component={RouteGuard}>
<Route path="/home" component={() => <AuthWrapper><Home/></AuthWrapper>} />
</Route>
</Router>
);
};
const RouteGuard: ParentComponent = (props) => {
const user = true; //Fetch User
const navigate = useNavigate();
if (!user) {
navigate("/auth/sign-in");
}
return (
<>{props.children}</>
);
};

const AuthWrapper: ParentComponent = (props) => (
<AuthProvider>{props.children}</AuthProvider>
);

const App: Component = () => {
return (
<Router>
<Route path="/auth" component={AuthLayout}>
<Route path="sign-in" component={SignInUp} />
</Route>
<Route path="/" component={RouteGuard}>
<Route path="/home" component={() => <AuthWrapper><Home/></AuthWrapper>} />
</Route>
</Router>
);
};
The example above shows the /home route receiving the user props throught the AuthWrapper component. But I would need to do that for every route. Is there some equivalent to this?
const App: Component = () => {
return (
<Router>
<Route path="/auth" component={AuthLayout}>
<Route path="sign-in" component={SignInUp} />
</Route>
<Route path="/" component={RouteGuard}>
<AuthWrapper>
{/* All routes in here can access the validated user data */}
<Route path="/home" component={Home} />
</AuthWrapper>
</Route>
</Router>
);
const App: Component = () => {
return (
<Router>
<Route path="/auth" component={AuthLayout}>
<Route path="sign-in" component={SignInUp} />
</Route>
<Route path="/" component={RouteGuard}>
<AuthWrapper>
{/* All routes in here can access the validated user data */}
<Route path="/home" component={Home} />
</AuthWrapper>
</Route>
</Router>
);
Also, I plan on using solid-query as the async state manager so it would be great if the solution could just use the user response from RouteGuard. This would just be a bonus though
1 Reply
GGboi.eth
GGboi.ethOP10mo ago
If anyone runs into this problem in the future, here is my solution
export type Session = { id: number; name: string };
//If we leave this undefined, then value passed through provider is also undefined
export const AuthContext = createContext<() => Session>();

const RouteGuard: ParentComponent = (props) => {
const navigate = useNavigate();
const query = createQuery<Session>(() => ({
queryKey: ["session"],
queryFn: () => {
return { id: 1, name: "Jimothy" };
},
}));

createEffect(() => {
if (!query.data && query.isFetched) {
navigate("/auth/sign-in");
}
});

return (
<Suspense fallback={"loading..."}>
<Show when={query.data} fallback={"no data"}>
<AuthContext.Provider value={() => query.data!}>
{props.children}
</AuthContext.Provider>
</Show>
</Suspense>
);
};

const queryClient = new QueryClient();
const App: Component = () => {
return (
<QueryClientProvider client={queryClient}>
<Router>
<Route path="/auth" component={AuthLayout}>
<Route path="sign-in" component={SignInUp} />
<Route path="sign-up" component={SignInUp} />
<Route path="verify" component={() => <>test3</>} />
</Route>
<Route path="/" component={RouteGuard}>
{/* Protected Routes from here on */}

<Route path="/home" component={Home} />
</Route>
</Router>
</QueryClientProvider>
);
};
export type Session = { id: number; name: string };
//If we leave this undefined, then value passed through provider is also undefined
export const AuthContext = createContext<() => Session>();

const RouteGuard: ParentComponent = (props) => {
const navigate = useNavigate();
const query = createQuery<Session>(() => ({
queryKey: ["session"],
queryFn: () => {
return { id: 1, name: "Jimothy" };
},
}));

createEffect(() => {
if (!query.data && query.isFetched) {
navigate("/auth/sign-in");
}
});

return (
<Suspense fallback={"loading..."}>
<Show when={query.data} fallback={"no data"}>
<AuthContext.Provider value={() => query.data!}>
{props.children}
</AuthContext.Provider>
</Show>
</Suspense>
);
};

const queryClient = new QueryClient();
const App: Component = () => {
return (
<QueryClientProvider client={queryClient}>
<Router>
<Route path="/auth" component={AuthLayout}>
<Route path="sign-in" component={SignInUp} />
<Route path="sign-up" component={SignInUp} />
<Route path="verify" component={() => <>test3</>} />
</Route>
<Route path="/" component={RouteGuard}>
{/* Protected Routes from here on */}

<Route path="/home" component={Home} />
</Route>
</Router>
</QueryClientProvider>
);
};
const Home: Component = (props) => {
//Need null ! here because initial state isn't defined. ts isn't smart enough to know it can't get here
//Can fail on hot module reload since only this comp is reloaded on save
const user = useContext(AuthContext)!;
return <div>Hello {user().name}</div>;
};

export default Home;
const Home: Component = (props) => {
//Need null ! here because initial state isn't defined. ts isn't smart enough to know it can't get here
//Can fail on hot module reload since only this comp is reloaded on save
const user = useContext(AuthContext)!;
return <div>Hello {user().name}</div>;
};

export default Home;
Want results from more Discord servers?
Add your server