React: 17.0.2
React Router: 6
Example: User are authenticated and try to access '/page2' through URL. They should fall through PrivateRoute flow and get to '/page2'.
Code:
const PublicRoutes = () => {
const { auth } = useAuth()
return auth ? <Navigate to={'/home'} replace /> : <Outlet />
}
const PrivateRoutes = () => {
const { auth } = useAuth()
return auth ? <Outlet /> : <Navigate to={'/signin'} replace />
}
export const Router = () => {
return (
<BrowserRouter>
<Routes>
<Route element={
<PublicLayout>
<PublicRoutes />
</PublicLayout>
}>
<Route path='/' element={<GetStarted />} />
<Route path='/signin' element={<SignIn />} />
<Route path='/signup' element={<SignUp />} />
</Route>
<Route element={
<PrivateLayout>
<PrivateRoutes />
</PrivateLayout>
}>
<Route path='/home' element={<Home />} />
<Route path='/page2' element={<Page2 />} />
</Route>
</Routes>
</BrowserRouter>
)
}
I've tried const location = useLocation()
and use location.pathname
but it gave some errors about multiple re-renders. Even the auth guide from Router 6 didnt work because location.state
is null.
Some info about my authenticante flow:
The PrivateRoutes
component needs to capture the current location and send this in route state to the page you are redirecting to for authentication, "/signin"
in this case.
const PrivateRoutes = () => {
const { auth } = useAuth();
const location = useLocation(); // <-- get current location being accessed
return auth
? <Outlet />
: (
<Navigate
to={'/signin'}
state={{ from: location }} // <-- pass in route state
replace
/>
);
};
Now, somewhere on your SignIn
component doing the authentication, access the passed from
value from route state and redirect accordingly.
const navigate = useNavigate();
const location = useLocation();
// Get redirect location or provide fallback
const from = location.state?.from || "/";
...
// in auth callback logic, once authenticated navigate (redirect) back
// to the route originally being accessed.
navigate(from, { replace: true });
I notice also your auth wrappers don't seem to account for the edge case where the app doesn't yet know the authentication status of the user, and in this case you want to conditionally render null or perhaps some loading indicator until the authContext
has resolved the auth
state.
Examples:
const PublicRoutes = () => {
const { auth } = useAuth();
if (auth === undefined) {
return null; // or loading spinner, etc...
}
return auth ? <Navigate to={'/home'} replace /> : <Outlet />
}
...
const PrivateRoutes = () => {
const { auth } = useAuth();
const location = useLocation();
if (auth === undefined) {
return null; // or loading spinner, etc...
}
return auth
? <Outlet />
: <Navigate to={'/signin'} state={{ from: location }} />;
};