I am using react-router-dom@6.3.0
I have created a React app where certain Private pages are accessible only users who have logged in.
You can find a demo here, and a GitHub repo here.
A simplified version of this is shown below.
I have wrapped every Private page in its own RequireLogin component, and this works:
<Route
path="/private1"
element={
<RequireLogin redirectTo="/">
<Private
text="Private Page #1"
/>
</RequireLogin >
}
/>
The RequireLogin component redirects to a page with the Login component if the user is not logged in, and renders the requested component only to a logged in user.
My question is this:
Is it there a way to wrap all the Private routes inside one RequireLogin component, or do I have to wrap each one separately?
import React, { createContext, useContext, useState } from 'react'
import ReactDOM from 'react-dom';
import {
BrowserRouter as Router,
Routes,
Route,
Navigate,
NavLink
} from "react-router-dom";
const UserContext = createContext()
const UserProvider = ({children}) => {
const [ loggedIn, logIn ] = useState("")
return (
<UserContext.Provider
value={{
loggedIn,
logIn
}}
>
{children}
</UserContext.Provider>
)
}
function App() {
return (
<Router>
<UserProvider>
<Routes>
<Route
path="/"
element={<NavLink to="/login">Log In</NavLink>}
/>
<Route
path="/login"
element={<Login />}
/>
<Route
path="/private1"
element={
<RequireLogin redirectTo="/login">
<Private
text="Private Page #1"
/>
</RequireLogin >
}
/>
<Route
path="/private2"
element={
<RequireLogin redirectTo="/login">
<Private
text="Private Page #2"
/>
</RequireLogin >
}
/>
</Routes>
</UserProvider>
</Router>
);
}
function Menu({hideLogOut}) {
const { loggedIn } = useContext(UserContext)
if (loggedIn) {
if (!hideLogOut) {
return <ul>
<li><NavLink to="/login">Log Out</NavLink></li>
<li><NavLink to="/private1">Private #1</NavLink></li>
<li><NavLink to="/private2">Private #2</NavLink></li>
</ul>
} else {
return <ul>
<li><NavLink to="/private1">Private #1</NavLink></li>
<li><NavLink to="/private2">Private #2</NavLink></li>
</ul>
}
} else {
return <p>Not Logged In</p>
}
}
function RequireLogin ({ children, redirectTo }) {
const { loggedIn } = useContext(UserContext);
return loggedIn
? children
: <Navigate to={redirectTo} />;
}
function Private({text}) {
return (
<div>
<Menu />
<h1>{text}</h1>
</div>
)
}
function Login() {
const { loggedIn, logIn } = useContext(UserContext)
const toggleLogged = () => {
logIn(!loggedIn)
}
return (<div>
<Menu
hideLogOut={true}
/>
<label htmlFor="loggedIn">
<input
type="checkbox"
name="loggedIn"
id="loggedIn"
checked={loggedIn}
onChange={toggleLogged}
/>
Pretend that we are logged in
</label>
</div>)
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
I use a second router for the private routes, wrapped with a single <RequireLogin>
. Example:
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route path="/register" element={<RegistrationPage />} />
<Route path="*" element={
<RequireLogin>
<Routes>
<Route path="/" element={<FeedPage />} />
<Route path="/explore" element={<ExplorePage />} />
<Route path="/user/:username" element={<UserPage />} />
<Route path="*" element={<Navigate to="/" />} />
</Routes>
</RequireLogin>
} />
</Routes>