Why unable to use protectRoute inside Router in react? - javascript

I have a react application in which users can access the homepage only after login/signup.
I have used protectRoutes to implement this , however I m getting the following error inside Router component:
A <Route> is only ever to be used as the child of <Routes> element, never rendered directly. Please wrap your <Route> in a <Routes>.
The code for app.js file:
import {useState} from 'react'
import './App.css';
import {Routes,Route,Link} from 'react-router-dom'
import SignupPage from './Pages/SignupPage';
import LoginPage from './Pages/LoginPage';
import Homepage from './Pages/Homepage';
import PrivateRoute from "./Pages/PrivateRoute";
import {HashRouter as Router} from 'react-router-dom'
function App() {
const [username,setUsername]=useState("");
const [currentUser,setCurrentUser]=useState({});
return (
<Router>
<div className="App">
<Route path="/" exact element={<SignupPage></SignupPage>}/>
<Route path="/signup" exact element={<SignupPage currentuser={currentUser} setcurrentuser={setCurrentUser} setusername={setUsername}></SignupPage>}/>
<Route path="/login" exact element={<LoginPage></LoginPage>}/>
<PrivateRoute path="/homepage" exact currentuser={currentUser} element={<Homepage user={currentUser} username={username} setusername={setUsername}></Homepage>}/>
</div>
</Router>
);
}
export default App;
Whats the error here and how do I fix it?

Issue
You are rendering Route components directly and are not wrapping them in a Routes component.
Solution
Refactor the PrivateRoute component to render an Outlet component instead of a Route and wrap the routes you want to protect.
Example:
import { Navigate, Outlet } from 'react-router-dom';
const PrivateRoute = ({ currentuser }) => {
// auth business logic
return isAuthenticated
? <Outlet />
: <Navigate to="/login" replace />;
};
...
import { HashRouter as Router, Routes, Route, Link} from 'react-router-dom'
function App() {
const [username,setUsername]=useState("");
const [currentUser,setCurrentUser]=useState({});
return (
<Router>
<div className="App">
<Routes>
... unprotected routes ...
<Route path="/" element={<SignupPage />} />
<Route
path="/signup"
element={(
<SignupPage
currentuser={currentUser}
setcurrentuser={setCurrentUser}
setusername={setUsername}
/>
)}
/>
<Route path="/login" element={<LoginPage />} />
...
<Route element={<PrivateRoute currentuser={currentUser} />}>
... protected routes ...
<Route
path="/homepage"
element={(
<Homepage
user={currentUser}
username={username}
setusername={setUsername}
/>
)}
/>
...
</Route>
</Routes>
</div>
</Router>
);
}

Related

React v6-Redux Create PrivateRoutes

Im trying to create a PrivateRoutes in addition to the regular routes.
After login, the page is successfully re-direct to /home, however when I tried to open /work, the page will go back to /home. all the data from state.valid is also shows "unidentified" in /work.
I figured it out that inside the privateRoutes it check if valid.isAuthenticated is true or not. However since. valid.isAuthenticated is set to false as initial value in reducer, everytime I open /home or /work, it re-render /login and then render /home or /work.
How do I fix to not to render /login before opening other pages?
Here is my PrivateRoutes.js
import React from "react";
import { useSelector } from "react-redux";
import { Navigate } from "react-router-dom";
const PrivateRoute = ({ children }) => {
const valid = useSelector((state) => state.valid);
return valid.isAuthenticated ? children : <Navigate to="/login" />;
};
export default PrivateRoute;
here is my AppRouter.js
import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import PrivateRoute from "./PrivateRoute";
import App from "../components/App";
import Login from "../components/Login";
import HomePage from "../components/HomePage";
import WorkPage from "../components/WorkPage";
const AppRouter = () => (
<BrowserRouter>
<div>
<NavigationBar />
<div>
<Routes>
<Route path="/" element={<App />} exact />
<Route path="/login" element={<Login />} exact />
<Route path="/home" element={<PrivateRoute><HomePage /></PrivateRoute>} />
<Route path="/work" element={<PrivateRoute><WorkPage /></PrivateRoute>} />
</Routes>
</div>
</div>
</BrowserRouter>
);
export default AppRouter;
useEffect(() => {
if (valid.isAuthenticated) {
navigate("/home");
}
},[valid.isAuthenticated]);
import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import PrivateRoute from "./PrivateRoute";
import App from "../components/App";
import Login from "../components/Login";
import HomePage from "../components/HomePage";
import WorkPage from "../components/WorkPage";
const AppRouter = () => {
useEffect(() => {
if (!valid.isAuthenticated) {
navigate("/login");
}
});
return (
<BrowserRouter>
<div>
<NavigationBar />
<div>
{valid.isAuthenticated ? <>
<Routes>
<Route path="/" element={<App />} exact />
<Route path="/home" element={<HomePage />} />
<Route path="/work" element={<WorkPage />} />
</Routes>
</>
:
<>
<Routes>
<Route path="/login" element={<Login />} exact />
</Routes>
</>
}
</div>
</div>
</BrowserRouter>
)
};
export default AppRouter;

problem with react router dom v6 subroutes

I am doing a practice exercise and I am creating sub-routes with react router dom v6, the problem I have is that the profile route has an authentication if it is authenticated it shows me the profile component otherwise it sends me to home, now to this /profile route I created a /exerciselist subroute but when I want to access /profile/exerciselist the component does not load me, it sends me directly to the /profile route, how can I make it load the profile/exerciselist route?
import React from 'react'
import { BrowserRouter, Route, Routes, Navigate} from "react-router-dom"
import { useContext } from "react"
import { authContext } from "./context/authContext"
import Homepage from "./pages/Homepage"
import Login from "./pages/Login"
import Register from "./pages/Register"
import Notfound from "./pages/Notfound"
import Profile from "./pages/Profile"
import Footer from "./components/footer"
import ExercisesList from './components/exercises_list'
import "./public/css/appStyles/appStyles.css"
function App() {
const { auth } = useContext(authContext)
return (
<BrowserRouter>
<Routes>
<Route path="/" element={!auth.auth ? <Homepage/> : <Navigate to="/profile" replace />}/>
<Route path="/register" element={ !auth.auth ? <Register/> : <Navigate to="/profile" replace />}/>
<Route path="/login" element={ !auth.auth ? <Login /> : <Navigate to="/profile" replace /> } />
<Route path="/profile/*" element={ auth.auth ? <Profile /> : <Navigate to="/" replace /> } >
<Route path="exerciselist" element={<ExercisesList/>} />
</Route>
<Route path="*" element={<Notfound/>}/>
</Routes>
<Footer/>
</BrowserRouter>
);
}
export default App;
When rendering nested routes you have a couple options.
Render an Outlet component in the parent route's component for the nested Route components to be rendered into.
Example:
import { Outlet } from 'react-router-dom';
...
const Profile = () => {
...
return (
<>
... Profile component JSX ...
<Outlet />
</>
);
};
Remove the trailing "*" since the nested route is rendered into Outlet.
<Route
path="/profile"
element={ auth.auth ? <Profile /> : <Navigate to="/" replace />}
>
<Route path="exerciselist" element={<ExercisesList/>} />
</Route>
Render a Routes and nested Route components directly in the routed component.
import { Routes, Route } from 'react-router-dom';
...
const Profile = () => {
...
return (
<>
... Profile component JSX ...
<Routes>
<Route path="exerciselist" element={<ExercisesList/>} />
</Routes>
</>
);
};
...
<Route
path="/profile/*" // <-- trailing * allows matching nested routes
element={ auth.auth ? <Profile /> : <Navigate to="/" replace />}
/>

why Passing Props in Private Route in ReactJs not working properly

Here is mycode of the file PrivateRoute.js
import React from "react";
import {Route,Redirect} from "react-router-dom"
import {isAuthenticated} from "./index"
const PrivateRoutes = (props)=>{
return(
isAuthenticated()?(<Route
render={ (props) =>
<component {...props}/>} />):
(<Redirect to={{ pathname:"/signin"}}/>)
// <h1>hey there</h1>
)
}
export default PrivateRoutes;
In this code it is saying that value of props is read but never used but iam using it in render function,destructuring is also not working here.
Here isAuthenticated() is my boolean function . If it evaluates to true i want to get on more route to user dashboard.
This is how my routes.js file looks like
import React from 'react'
import {BrowserRouter,Route,Switch} from "react-router-dom";
import AdminRoutes from './auth/helper/AdminRoutes';
import PrivateRoutes from './auth/helper/PrivateRoutes';
import Home from './core/Home';
import AdminDashboard from './user/AdminDashBoard';
import Signin from './user/Signin';
import Signup from './user/Signup';
import UserDashboard from './user/UserDashBoard';
function Routes() {
return (
<BrowserRouter>
<Switch>
<Route exact path='/' component={Home} />
<Route exact path="/signup" component={Signup} />
<Route exact path="/signin" component={Signin} />
<PrivateRoutes component="UserDashboard" exact path="/user/dashboard"/>
<AdminRoutes exact path="/admin/dashboard" component={AdminDashboard}/>
</Switch>
</BrowserRouter>
)
}
export default Routes
Help me to solve this problem.
This is because you have declared *two functions, each taking a props object argument, but only the inner/nested function's props is referenced.
Rename the nested props to something else like routeProps and spread both to the rendered component. Remember also that valid React component names are PascalCased.
Example:
const PrivateRoutes = ({ component: Component, ...props }) => {
return isAuthenticated()
? (
<Route
render={(routeProps) => (
<Component {...props} {...routeProps} />
)}
/>
) : <Redirect to={{ pathname:"/signin"}}/>;
}
Then also fix the PrivateRoutes component use to pass a value React component reference instead of a string.
<Switch>
<Route path="/signup" component={Signup} />
<Route path="/signin" component={Signin} />
<PrivateRoutes component={UserDashboard} path="/user/dashboard" />
<AdminRoutes path="/admin/dashboard" component={AdminDashboard} />
<Route path='/' component={Home} />
</Switch>

How can I redirect in React Router v6?

I am trying to upgrade to React Router v6 (react-router-dom 6.0.1).
Here is my updated code:
import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/lab" element={<Lab />} />
<Route render={() => <Navigate to="/" />} />
</Routes>
</BrowserRouter>
The last Route is redirecting the rest of paths to /.
However, I got an error
TS2322: Type '{ render: () => Element; }' is not assignable to type 'IntrinsicAttributes & (PathRouteProps | LayoutRouteProps | IndexRouteProps)'.   Property 'render' does not exist on type 'IntrinsicAttributes & (PathRouteProps | LayoutRouteProps | IndexRouteProps)'.
However, based on the documentation, it does have render for Route. How can I use it correctly?
I think you should use the no match route approach.
Check this in the documentation: Adding a "No Match" Route
import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/lab" element={<Lab />} />
<Route
path="*"
element={<Navigate to="/" replace />}
/>
</Routes>
</BrowserRouter>
To keep the history clean, you should set replace prop. This will avoid extra redirects after the user click back.
I found another way to do this:
import { useNavigate } from "react-router-dom";
let navigate = useNavigate();
useEffect(() => {
if (LoggedIn){
return navigate("/");
}
},[LoggedIn]);
See Overview, Navigation.
Create the file RequireAuth.tsx
import { useLocation, Navigate } from "react-router-dom";
import { useAuth } from "../hooks/Auth";
export function RequireAuth({ children }: { children: JSX.Element }) {
let { user } = useAuth();
let location = useLocation();
if (!user) {
return <Navigate to="/" state={{ from: location }} replace />;
} else {
return children;
}
}
Import the component to need user a private router:
import { Routes as Switch, Route } from "react-router-dom";
import { RequireAuth } from "./RequireAuth";
import { SignIn } from "../pages/SignIn";
import { Dashboard } from "../pages/Dashboard";
export function Routes() {
return (
<Switch>
<Route path="/" element={<SignIn />} />
<Route
path="/dashboard"
element={
<RequireAuth>
<Dashboard />
</RequireAuth>
}
/>
</Switch>
);
}
In version 5 of React, i.e., react-router-dom, we had the Redirect component. But in version 6 of React it is updated to the Navigate components.
We can pass replace in these components to avoid unnecessary redirects on clicking back and forward option.
Demonstration for usage is attached below:
<Route path="/" element={user ? <Home /> : <Register />} />
<Route path="/login" element={user ? <Navigate to="/" replace /> : <Login />} />
<Route path = "/register" element={user ? <Navigate to="/" replace /> : <Register />} />
FOR react-router VERSION 6
New example after editing----->(more simple easy to read)
import {BrowserRouter as Router,Routes,Route} from 'react-router-dom';
import Home from '../NavbarCompo/About';
import Contact from '../NavbarCompo/Contact';
import About from '../NavbarCompo/About';
import Login from '../NavbarCompo/Login';
import Navbar from '../Navbar/Navbar';
import Error from '../pages/error';
import Products from '../pages/products';
import Data from '../NavbarCompo/Data';
const Roter=()=>{
return (
<Router>
<Navbar />
<Routes>
<Route path='/' element={<Home />} />
<Route path='/about' element={<About />} />
<Route path='/contact' element={<Contact />} />
<Route path='/login' element={<Login />} />
<Route path='/product/:id' element={<Products />} />
<Route path='/data' element={<Data />} />
<Route path ='*' element={<Error />} />
</Routes>
</Router>
)
}
export default Roter;
Look at the example
import React from "react";
import Form from "./compo/form";
import ReactDOM from "react-dom/client";
import { createBrowserRouter, RouterProvider, Route,Routes,Navigate } from "react-router-dom";
const router = createBrowserRouter([
{
path: "/",
element: <Form />
},
{
path: "/about",
element: <h1>hola amigo,you are in about section</h1>
},
{
path:"*",
element:<Navigate to="/" replace />
}
]);
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<RouterProvider router={router}></RouterProvider>
</React.StrictMode>
);
check this out
https://reactrouter.com/en/main/start/overview
<BrowserRouter>
<Routes>
<Route path="home" element={<Home />} />
<Route path="about" element={<About />} />
<Route index element={<Navigate to="/home" replace />} />
</Routes>
</BrowserRouter>
import { useNavigate } from "react-router-dom";
import { Button } from "#mui/material";
const component =()=>{
const navigate = useNavigate();
const handelGoToLogin = () => {
navigate('/auth/login')
}
return(<>
//.........
<Button onClick={handelGoToLogin} variant="outlined" color="primary" size="large" fullWidth>
Back
</Button>
</>)
}
import { useState } from "react"
import { Navigate } from "react-router-dom"
const [login, setLogin] = useState(true)
return (<>
{!login && <Navigate to="/login" />}
<>)
For class components, at the first you should make a functional component, and then use HOC technical to use useNavigate React hook.
Like this:
File withrouter.js
import {useNavigate} from 'react-router-dom';
export const withRouter = WrappedComponent => props => {
return (<WrappedComponent {...props} navigate={useNavigate()}/>);
};
Then use it in other class components like this:
export default withRouter(Signin);
And use props for redirect like this:
this.props.navigate('/');

Build independant admin router with react

I've built an app with its specific router. Now I want to improve my app by coding an admin interface. The thing is, I have components (Navbar and Footer) that surround my routes (see code below).
So if I simply create an admin interface and nest it inside my existing router, my app's navbar and footer will appear on the admin pages.
I would like to code my admin interface with its own navbar and components.
Is there a way to do so ?
AppRouter.js:
import React from 'react';
import { Router, Route, Switch } from 'react-router-dom';
import createBrowserHistory from 'history/createBrowserHistory';
import LandingPage from '../ui/landing-page/LandingPage';
import App from '../ui/App';
import NotFoundPage from '../ui/NotFoundPage';
import NavBar from '../ui/NavBar';
import Footer from '../ui/Footer';
import FaqPage from '../ui/FaqPage';
import PrivacyPage from'../ui/PrivacyPage';
import LegalNoticePage from '../ui/LegalNoticePage';
const browserHistory = createBrowserHistory();
export const AppRouter = () => (
<Router history={browserHistory}>
<div>
<NavBar />
<Switch>
<Route exact path="/" component={LandingPage}/>
<Route exact path="/meals" component={App}/>
<Route exact path="/faq" component={FaqPage}/>
<Route exact path="/privacy" component={PrivacyPage}/>
<Route exact path="/legal_notice" component={LegalNoticePage}/>
<Route component={NotFoundPage}/>
</Switch>
<Footer />
</div>
</Router>
);
export default AppRouter;
Your <NavBar /> isn't surrounding your <Router>, it's surrounding your routes. Since you can nest Routers, you could have a <Route> which matches all path="/admin" and then within that put the admin-only NavBar.
You can read more about nested routes here.
Here's a code example:
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, Switch, Route } from "react-router-dom";
const Admin = ({ match }) => (
<React.Fragment>
<h1>admin bar</h1>
<Route path={`${match.path}/1`} render={() => <h2>one</h2>} />
<Route path={`${match.path}/2`} render={() => <h2>two</h2>} />
</React.Fragment>
);
const Other = ({ match }) => (
<React.Fragment>
<h1>other bar</h1>
<Switch>
<Route path={`${match.path}/2`} render={() => <h2>one</h2>} />
<Route path={`${match.path}/2`} render={() => <h2>two</h2>} />
</Switch>
</React.Fragment>
);
const App = () => (
<BrowserRouter>
<Switch>
<Route path="/admin" component={Admin} />
<Route path="/other" component={Other} />
</Switch>
</BrowserRouter>
);
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
And accompanying CodeSandbox.

Categories

Resources