Can anyone tell me what's wrong in this code ??
I am protecting the route but the error is comming .
I have creted a ProtectionRoute component which calls AUTH_FUNC(Checks whether user is locked in or not) from the TwitterState.js and if the user is logged in then the ProtectionRoute returns the Component and else redirect to the login page !
App.js
import {BrowserRouter as Router , Routes , Route , useNavigate} from "react-router-dom"
import Home from "./pages/Home"
import Auth from "./pages/Auth"
import Profile from "./pages/Profile"
import Bookmark from "./pages/Bookmark"
import NotFound from "./pages/NotFound"
import Explore from "./pages/Explore"
import TrendingTags from "./pages/TrendingTags"
import Discover from "./pages/Discover"
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import Register from "./pages/Register"
import EditProfile from "./pages/EditProfile"
import ProtectedRoute from "./components/ProtectedRoute"
function App() {
return (
<Router>
<ToastContainer />
<Routes>
<Route path="/" element={<ProtectedRoute><Home/></ProtectedRoute>}/>
<Route path="/auth" element={<ProtectedRoute><Auth/></ProtectedRoute>}/>
<Route path="/profile" element={<ProtectedRoute><Profile/></ProtectedRoute>} />
<Route path="/message" element={<ProtectedRoute><NotFound/></ProtectedRoute>} />
<Route path="/notifications" element={<ProtectedRoute><NotFound/></ProtectedRoute>} />
<Route path="/bookmark" element={<ProtectedRoute><Bookmark/></ProtectedRoute>} />
<Route path="/explore" element={<ProtectedRoute><Explore/></ProtectedRoute>} />
<Route path="/explore/trending/:tagName" element={<ProtectedRoute><TrendingTags/></ProtectedRoute>} />
<Route path="/discover" element={<ProtectedRoute><Discover/></ProtectedRoute>} />
<Route path="/register" element={<ProtectedRoute><Register /></ProtectedRoute>} />
<Route path="/profile/edit" element={<ProtectedRoute><EditProfile /></ProtectedRoute>} />
</Routes>
</Router>
);
}
export default App;
ProtectedRoute.js
import React , {useContext} from "react";
import { Route, Navigate } from "react-router-dom";
import TwitterContext from "../context/TwitterContext";
export default function ProtectedRoute ({children}) {
const {AUTH_FUNC} = useContext(TwitterContext)
const loggedin = AUTH_FUNC()
if(!loggedin){
return <Navigate to="/auth"/>
}
return children
};
Twitter.jsx
import TwitterContext from "./TwitterContext";
const TwitterState = (props) => {
const AUTH_FUNC = () =>{
const res = JSON.parse(localStorage.getItem("UserData"))
if(res !== null){
return true
}
return false
}
return <TwitterContext.Provider value={{AUTH_FUNC}}>
{props.children}
</TwitterContext.Provider>
}
export default TwitterState
Error :
Issue
You are protecting the "/auth" route as well, which when a user is not authenticated yet will create a navigation loop from "/auth" to "/auth", repeat ad nauseam.
function App() {
return (
<Router>
<ToastContainer />
<Routes>
...
<Route path="/auth" element={<ProtectedRoute><Auth/></ProtectedRoute>}/>
...
</Routes>
</Router>
);
}
export default function ProtectedRoute ({ children }) {
const { AUTH_FUNC } = useContext(TwitterContext);
const loggedin = AUTH_FUNC();
if (!loggedin) {
return <Navigate to="/auth"/>;
}
return children;
};
Solution
You don't want to protect the authentication route the same way as the routes that require authentication. Remove ProtectedRoute from the "/auth" route.
Refactor the ProtectedRoute to render an Outlet also so you can make the code more DRY. This allows the ProtectedRoute component to wrap entire sets of routes that need to be protected.
import { Navigate, Outlet } from 'react-router-dom';
export default function ProtectedRoute () {
const { AUTH_FUNC } = useContext(TwitterContext);
const loggedin = AUTH_FUNC();
if (loggedin === undefined) {
return null; // or loading indicator/spinner/etc
}
return loggedin
? <Outlet />
: <Navigate to="/auth" replace />;
};
function App() {
return (
<Router>
<ToastContainer />
<Routes>
{/* Unprotected routes */}
<Route path="/auth" element={<Auth />} />
<Route path="/register" element={<Register />} />
{/* Protected routes */}
<Route element={<ProtectedRoute />}>
<Route path="/" element={<Home />} />
<Route path="/profile" element={<Profile />} />
<Route path="/message" element={<NotFound />} />
<Route path="/notifications" element={<NotFound />} />
<Route path="/bookmark" element={<Bookmark />} />
<Route path="/explore" element={<Explore />} />
<Route path="/explore/trending/:tagName" element={<TrendingTags />} />
<Route path="/discover" element={<Discover />} />
<Route path="/profile/edit" element={<EditProfile />} />
</Route>
</Routes>
</Router>
);
}
It's also a common pattern to protect the "anonymous" routes from authenticated users. For this create another protected route component that does the inverse of the ProtectedRoute component.
import { Navigate, Outlet } from 'react-router-dom';
export default function AnonymousRoute () {
const { AUTH_FUNC } = useContext(TwitterContext);
const loggedin = AUTH_FUNC();
if (loggedin === undefined) {
return null; // or loading indicator/spinner/etc
}
return loggedin
? <Navigate to="/" replace />
: <Outlet />;
};
function App() {
return (
<Router>
<ToastContainer />
<Routes>
{/* Anonymous routes */}
<Route element={<AnonymousRoute />}>
<Route path="/auth" element={<Auth />} />
<Route path="/register" element={<Register />} />
</Route>
{/* Protected routes */}
<Route element={<ProtectedRoute />}>
<Route path="/" element={<Home />} />
<Route path="/profile" element={<Profile />} />
<Route path="/message" element={<NotFound />} />
<Route path="/notifications" element={<NotFound />} />
<Route path="/bookmark" element={<Bookmark />} />
<Route path="/explore" element={<Explore />} />
<Route path="/explore/trending/:tagName" element={<TrendingTags />} />
<Route path="/discover" element={<Discover />} />
<Route path="/profile/edit" element={<EditProfile />} />
</Route>
</Routes>
</Router>
);
}
The Auth is in protected route and causes the navigation loop.
<Route path="/auth" element={<ProtectedRoute><Auth/></ProtectedRoute>}/>
export default function ProtectedRoute ({children}) {
const {AUTH_FUNC} = useContext(TwitterContext)
const loggedin = AUTH_FUNC()
if(!loggedin){
return <Navigate to="/auth"/>
}
return children
};
/auth route probably shouldn't be a protected route, in order to function normally.
Related
I have written AuthContext.js in a seprate file like bellow
export const AuthContext = createContext({
isLoggedIn: false,
login: () => {},
logout: () => {},
});
const AuthContextProvider = (props) => {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const loginHandler = useCallback(() => {
setIsLoggedIn(true);
}, []);
const logoutHandler = useCallback(() => {
setIsLoggedIn(false);
}, []);
const initialValue = {
isLoggedIn: isLoggedIn,
login: loginHandler,
logout: logoutHandler,
};
return (
<AuthContext.Provider value={initialValue}>
{props.children}
</AuthContext.Provider>;
);
};
export default AuthContextProvider;
Then I import them manually in App.js
I recognized that when do I login, App.js never re-render even the value of isLoggedIn changed.
Result in routes variable never gotten new values, components will not change as well.
The App.js file
const App = () => {
const authCtx = useContext(AuthContext);
const { isLoggedIn } = authCtx;
let routes;
if (isLoggedIn) {
routes = (
<Switch>
<Route path="/" exact>
<Users />
</Route>
<Route path="/places/new" exact>
<NewPlace />
</Route>
<Route path="/:userId/places" exact>
<UserPlaces />
</Route>
<Route path="/places/:placeId">
<UpdatePlace />
</Route>
<Redirect to="/" />
</Switch>
);
}
else {
routes = (
<Switch>
<Route path="/" exact>
<Users />
</Route>
<Route path="/:userId/places" exact>
<UserPlaces />
</Route>
<Route path="/signin" exact>
<SignIn />
</Route>
<Route path="/signup" exact>
<Signup />
</Route>
<Redirect to="/signin" />
</Switch>
);
}
return (
<AuthContextProvider>
<BrowserRouter>
<MainNavigation />
<main>{routes}</main>
</BrowserRouter>
</AuthContextProvider>
);
};
export default App;
What is my mistake in this case.
If App is the component rendering the AuthContextProvider component then it can't access the Context value it provides. The AuthContextProvider must be rendered higher in the ReactTree in order for App to be able to access the Context value. Current App is receiving the default value that was passed to React.createContext.
export const AuthContext = createContext({
isLoggedIn: false,
login: () => {},
logout: () => {},
});
The default value is used when there is not any Context provider higher in the ReactTree.
Move AuthContextProvider to be higher in the ReactTree, i.e. wrap App.
App
const App = () => {
const { isLoggedIn } = useContext(AuthContext);
let routes;
if (isLoggedIn) {
routes = (
<Switch>
<Route path="/" exact>
<Users />
</Route>
<Route path="/places/new" exact>
<NewPlace />
</Route>
<Route path="/:userId/places" exact>
<UserPlaces />
</Route>
<Route path="/places/:placeId">
<UpdatePlace />
</Route>
<Redirect to="/" />
</Switch>
);
}
else {
routes = (
<Switch>
<Route path="/" exact>
<Users />
</Route>
<Route path="/:userId/places" exact>
<UserPlaces />
</Route>
<Route path="/signin" exact>
<SignIn />
</Route>
<Route path="/signup" exact>
<Signup />
</Route>
<Redirect to="/signin" />
</Switch>
);
}
return (
<>
<MainNavigation />
<main>{routes}</main>
</>
);
};
export default App;
<AuthContextProvider>
<BrowserRouter>
<App />
</BrowserRouter>
</AuthContextProvider>
const Navbar = () => {
return (
<div>
{location === '/' ? (
<AuthNav />
) : location === '/home' && isAuthenticated ? (
<MainNav />
) : <AuthNav />
}
</div>
);
};
How do I render two separate navbars on different application routes, in this case, I want to render the AuthNav in the login and signup path and I want to render MainNav on the home path.
Issues
I think you've a few things working against you:
The Navbar component is unconditionally rendered and using window.location.pathname to compute which actual navigation component to render. This means the view to be rendered is only computed when the Navbar component rerenders.
The Navbar component is rendered outside the Routes, so it's not rerendered when a route changes.
Solution
Instead of unconditionally rendering Navbar and trying to compute which nav component to render based on any current URL pathname, split them out into discrete layout routes that render the appropriate nav component.
Example:
Navbar.jsx
export const AuthNav = ({ auth }) => {
....
};
export const MainNav = () => {
....
};
App.jsx
import { Routes, Route, Navigate, Outlet } from 'react-router-dom';
import { useState } from "react";
// components
import { AuthNav, MainNav } from './components/Navbar';
// pages
...
...
const AuthLayout = ({ auth }) => (
<>
<AuthNav auth={auth} />
<Outlet />
</>
);
const MainLayout = () => (
<>
<MainNav />
<Outlet />
</>
);
const PrivateRoute = ({ auth }) => {
return auth.isAuthenticated
? <Outlet />
: <Navigate to="/" replace />;
};
const App = () => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
return (
<div className='parent'>
<Routes>
<Route element={<AuthLayout auth={{ isAuthenticated, setIsAuthenticated }} />}>
<Route path='/' element={<SignIn />} />
<Route path='/signup' element={<SignUp />} />
</Route>
<Route element={<MainLayout />}>
<Route element={<PrivateRoute auth={{ isAuthenticated }} />}>
<Route path='/Home' element={<Home />} />
<Route path='/music' element={<Music />} />
<Route path='/genre/' element={<Pop />} />
<Route path='/Hiphop' element={<HipHop />} />
<Route path='/Rock' element={<Rock />} />
<Route path='/EDM' element={<EDM />} />
<Route path='/Jazz' element={<Jazz />} />
<Route path='/RandB' element={<RandB />} />
<Route path='/store' element={<Store />} />
<Route path='/News' element={<News />} />
<Route path='/Contact' element={<Contact />} />
<Route path='/album/:id' element={<Album />} />
<Route path ="/album/:id/nested/" element={<Albums2 />} />
</Route>
</Route>
</Routes>
</div>
);
};
This question already has answers here:
How to create a protected route with react-router-dom?
(5 answers)
Closed 4 months ago.
Help
I'm using a protected route in my React app. But it is not working. All the other Elements are working but when I got to "/account" the whole screen is white. This is my code. It will be really helpful for me if you give that answer. Thank You :)
Protected Route code:
import React, { Fragment } from 'react';
import { useSelector } from 'react-redux';
import { Route, Routes, redirect } from 'react-router-dom';
const ProtectedRoute = ({ element: Element, ...rest }) => {
const { loading, isAuthenticated, user } = useSelector(state => state.user);
return (
<Fragment>
{!loading &&
(<Routes>
<Route
{...rest}
render={(props) => {
if(!isAuthenticated) {
return redirect("/login")
}
return <Element {...props} />
}}
/>
</Routes>
)}
</Fragment>
)
}
export default ProtectedRoute;
I am using ProtectedRoute.js in App.js. Here is the code.
App.js Code:
import React from 'react';
import {BrowserRouter as Router,Route,Routes} from "react-router-dom";
import './App.css';
import Header from "./component/layout/Header/Header.js";
import webFont from "webfontloader";
import Footer from './component/layout/Footer/Footer';
import Home from "./component/Home/Home.js";
import ProductDetails from "./component/Product/ProductDetails.js";
import Products from "./component/Product/Products.js";
import Search from "./component/Product/Search.js";
import LoginSignUp from './component/User/LoginSignUp';
import store from "./store";
import { loadUser } from './action/userAction';
import UserOption from "./component/layout/Header/UserOption.js";
import { useSelector } from 'react-redux';
import Profile from "./component/User/Profile.js"
import ProtectedRoute from './component/Route/ProtectedRoute';
function App() {
const {isAuthenticated, user} = useSelector(state => state.user)
React.useEffect(() => {
webFont.load({
google:{
families:["Roboto","Droid Sans","Chilanka"]
},
});
store.dispatch(loadUser())
}, [])
return (
<Router>
<Header />
{isAuthenticated && <UserOption user={user} />}
<Routes>
<Route path="/" element={<Home />} />
<Route path="/product/:id" element={<ProductDetails />} />
<Route path="/products" element={<Products />} />
<Route path="/products/:keyword" element={<Products />} />
<Route path="/search" element={<Search />} />
<Route path="/account" element={ <ProtectedRoute> <Profile /> </ProtectedRoute> } />
<Route path="/login" element={<LoginSignUp />} />
</Routes>
<Footer />
</Router>
);
}
export default App;
In your App.js you can declare the protected route like this
<Route path="/account" element={ <ProtectedRoute /> } >
<Route path="/account" element={ <Profile /> } >
</Route>
You can use Outlet of react-router v6 for passing the Component
const ProtectedRoute = ({ element: Element, ...rest }) => {
const { loading, isAuthenticated, user } = useSelector(state => state.user);
if (loading) {
return <h2>Loading...</h2>
}
return isAuthenticated ? <Outlet /> : <Navigate to="/login" />;
}
# Sayedul Karim.
I have a better and more concise way for this, An example of the code is below.
<Routes>
<Route element={<App />}>
{isAuthenticated ? (
<>
<Route path="/*" element={<PrivateRoutes />} />
<Route
index
element={<Navigate to="/account" />}
/>
</>
) : (
<>
<Route path="auth/*" element={<AuthPage />} />
<Route path="*" element={<Navigate to="/auth" />} />
</>
)}
</Route>
</Routes>
In this way, you don't have to make any private component instead just make a component for private routes where the routes are defined.
The PrivateRoutes component will be like this
<Routes>
<Route>
{/* Redirect to account page after successful login */}
{/* Pages */}
<Route path="auth/*" element={<Navigate to="/account" />} />
<Route path="account" element={<Account />} />
</Routes>
If any query further, feel free to ask....
I'm not sure, but you haven't pass any "element" prop to your ProtectedRoute component. You pass Profile component as children, so try render children instead of element in ProtectedRoute if you want your code to work like this.
I believe that you might want not to nest those routes, so also you might want to try use ProtectedRoute as Route in your router, I'm talking about something like this
<Routes>
...
<ProtectedRoute path="/account" element={<Profile />} />
...
</Routes>
UPDATE
It might show you this error because your Route is conditionally rendered, so try to handle loading state in some other way, maybe something like this
return (
<Route
{...rest}
render={(props) => {
if(!isAuthenticated) {
return redirect("/login")
}
if(loading) {
return <LoadingComponent />
}
return <Element {...props} />
}}
/>
)
I'm having some kind of trouble when I'm using Router in App.js
I'm getting a blank page when I am using, I tried a lot but couldn't found a way to solve the issue.
<GuestRoute path="/Authenticate" element={<Authenticate />}>
</GuestRoute>
it is working fine with
<Route path="/Authenticate" element={<Authenticate />}>
</Route>
but I have to use GuestRoute.
Given below is the whole code:
App.js
import "./App.css";
import {BrowserRouter as Router, Routes, Route, Navigate } from "react-router-dom";
import React from "react"
import Navigation from "./components/shared/Navigation/Navigation";
import Home from "./Pages/Home/Home";
import Register from "./Pages/Register/Register";
import Login from "./Pages/Login/Login";
import Authenticate from "./Pages/Authenticate/Authenticate";
const isAuth = true;
function App() {
return (
<div className="App">
<Router>
<Navigation />
{/* switch(prev. versions) ----> Routes (new versions)) */}
<Routes>
<Route exact path="/" element={<Home />} >
</Route>
<GuestRoute path="/Authenticate" element={<Authenticate />}>
</GuestRoute>
</Routes>
</Router>
</div>
);
}
const GuestRoute = ({children,...rest}) => {
return(
<Route {...rest}
render={({location})=>{
return isAuth ? (
<Navigate to={{
pathname: '/rooms',
state: {from: location}
}}
/>
):(
children
);
}}
></Route>
);
};
export default App;
react-router-dom#6 doesn't use custom route components. The new pattern used in v6 are either wrapper components or layout route components.
Wrapper component example:
const GuestWrapper = ({ children }) => {
... guest route wrapper logic ...
return (
...
{children}
...
);
};
...
<Router>
<Navigation />
<Routes>
<Route path="/" element={<Home />} />
<Route
path="/Authenticate"
element={(
<GuestWrapper>
<Authenticate />
</GuestWrapper>
)}
/>
</Routes>
</Router>
Layout route component example:
import { Outlet } from 'react-router-dom';
const GuestLayout = () => {
... guest route wrapper logic ...
return (
...
<Outlet /> // <-- nested routes render here
...
);
};
...
<Router>
<Navigation />
<Routes>
<Route path="/" element={<Home />} />
<Route element={<GuestLayout>}>
<Route path="/Authenticate" element={<Authenticate />} />
... other GuestRoute routes ...
</Route>
</Routes>
</Router>
this Protected route is works well for profile page. but i want to make a protected route for multiple page. in my project , i want to implement protected route for 'user profile update page', "update password" and 'order details page' but i can not implement this.
ProtectedRoute.js
import React, { Fragment } from "react";
import { useSelector } from "react-redux";
import { Navigate, Route } from "react-router-dom";
import { Outlet } from 'react-router-dom';
const ProtectedRoute = () => {
const { loading, isAuthenticated, user } = useSelector((state) => state.user);
if (loading) return null;
return isAuthenticated
? <Outlet />
: <Navigate to="/login" replace />;
};
export default ProtectedRoute;
App.js
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import './App.css';
import Header from './components/Header/Header';
import React from 'react';
import Service from './components/Service/Service'
import Home from './components/Home/Home';
import About from './components/About/About'
import Footer from './components/Footer/Footer';
import Login from './components/User/Login';
import Cart from './components/Cart/Cart';
import ProductDetails from './components/Product/ProductDetails';
import ScrollToTop from './components/ScrollToTop/ScrollToTop';
import Signup from './components/User/Signup';
import Shipping from './components/Cart/Shipping';
import Profile from './components/User/Profile';
import store from './store';
import { loadUser } from './Actions/userAction';
import UserOptions from './components/Header/UserOptions';
import { useSelector } from 'react-redux';
import ProtectedRoute from './components/Route/ProtectedRoute';
import UpdateProfile from './components/User/UpdateProfile'
import ForgotPassword from './components/User/ForgotPassword';
import ResetPassword from './components/User/ResetPassword';
import MyOrders from './components/Order/MyOrders';
function App() {
const { isAuthenticated, user } = useSelector((state) => state.user);
React.useEffect(() => {
store.dispatch(loadUser());
}, []);
return (
<Router>
<ScrollToTop />
<Header />
{isAuthenticated && <UserOptions user={user} />}
<Routes>
<Route exact path='/' element={<Home />} ></Route>
<Route exact path='/about' element={<About />} ></Route>
<Route exact path='/service' element={<Service />} ></Route>
<Route exact path='/login' element={<Login />} ></Route>
<Route exact path='/signup' element={<Signup />} ></Route>
<Route path="/account" element={<ProtectedRoute />}>
<Route path="/account" element={<Profile />} />
</Route>
<Route exact path="/password/forgot" element={<ForgotPassword />} />
<Route exact path="/password/reset/:token" element={<ResetPassword />} />
<Route exact path='/cart' element={<Cart />} ></Route>
<Route exact path='/product/:id' element={<ProductDetails />} ></Route>
<Route exact path='/shipping' element={<Shipping />}></Route>
</Routes>
<Footer />
</Router>
);
}
export default App;
i tried this but it didn't work
<Route path="/account" element={<ProtectedRoute/>}>
<Route path="/account" element={<Profile/>} />
<Route path="me/update" element={<UpdateProfile/>} />
<Route path="/orders" element={<MyOrders/>} />
</Route>
try this instead:
<Route path="/account" element={<ProtectedRoute><Profile/></ProtectedRoute>}></Route>
<Route path="me/update" element={<ProtectedRoute><UpdateProfile/></ProtectedRoute>}></Route>
Instead of wrapping each element with ProtectedRoute, do it at once
Use like this:
<Route element={<ProtectedRoute/>}>
<Route path="/account" element={<Profile/>} />
<Route path="me/update" element={<UpdateProfile/>} />
<Route path="/orders" element={<MyOrders/>} />
</Route>