Restriction on pages after login in React - javascript

So I'm using react router v6 in my React App.
I have the following routes enabled in my app.js file
<Routes>
<Route path='/' component={<Home />} />
<Route path='/login' component={<SignUp />} />
<Route path='/signup' component={<Login />} />
</Routes>
Everything's fine and that. What I want to to do is to put restriction on pages. Now I know how to create PrivateRoutes and PublicRoutes based on LoggedIn User.
For this purpose I want the user to not be able to access Homepage after he or she signups.
Are there an functions for that or what strategy would I use.

I accomplished this using 'react-router-dom' and creating a PrivateRoute component. The following code is not tested but can give you some ideas. LoaderComponent is a loading animation of your choice can be toher component as well.
// Based on https://reactrouter.com/web/example/auth-workflow
// If the user is not yet authenticated.
const PrivateRoute: React.FC<PrivateRouteProps> = ({ children, path, ...props }) => {
const [isAuthenticated, setIsAuthenticated] = useState(false);
useEffect(() => {
//put your authentication logic
setIsAuthenticated(true);
}, []);
return (
<Route
path={path}
{...props}
render={() => (isAuthenticated ? children : <LoaderComponent />)}
/>
);
};
And in your router config
import { Switch } from 'react-router-dom';
<Switch>
<PrivateRoute exact path='/'>
<Home />
</PrivateRoute>
...
</Switch>

Related

Navigate in different Routes React

I have an authentication system, and I want to show different <Routes> with different available paths considering from login state.
I uesd <Navigate/> element for redirection from hidden pages depending login state. But there is a problem. <Navigate/> redirects without considering the state.
For example, when I logged in and try to open Login page I must redirects to a Main page, and when I don't logged in and try to open profile I must redirect to Login Page. And when I try to open any of this pages I automaticly redirects to Main page.
routes.jsx:
import React from 'react';
import {
Routes,
Route,
Navigate
} from 'react-router-dom';
import Profile from './pages/Profile/Main/Profile';
import Login from './pages/Auth/Login/Login';
import Register from './pages/Auth/Register/Register';
import Main from './pages/Main/Main';
import { Loans } from './pages/Profile/Active/Loans';
import ErrorPage from './pages/errorPage/ErrorPage';
export const useRoutes = isAuthenticated => {
if(isAuthenticated){
return (
<Routes>
<Route path='/profile' exact>
<Route index path=":id" element={<Profile/>}/>
<Route path="loans" element={<Loans/>} exact/>
</Route>
<Route path='/' exact element={<Main/>}/>
<Route
path="*"
element={<ErrorPage/>}
/>
<Route
path="/auth/*"
element={<Navigate to="/" replace />}
/>
</Routes>
);
} else {
return (
<Routes>
<Route path='/auth' exact>
<Route path='login' element={<Login/>} exact />
<Route path="register" exact element={<Register/>}/>
<Route
path=""
element = {
<Navigate to="login" replace />
}
/>
</Route>
<Route path='/' exact element={<Main/>}/>
<Route
path="/profile/*"
element={<Navigate to="/auth/login" replace />}
/>
<Route
path="*"
element={<ErrorPage/>}
/>
</Routes>
)
}
}
App.jsx:
import {
BrowserRouter
} from 'react-router-dom';
import {useRoutes} from './routes';
import 'materialize-css';
import { useAuth } from './hooks/auth.hook';
import { AuthContext } from './context/auth.context';
function App() {
const {token, userId, login, logout} = useAuth();
const isAuthenticated = !!token;
const routes = useRoutes(isAuthenticated);
return (
<AuthContext.Provider value = {{
token, login, logout, userId, isAuthenticated
}}>
<BrowserRouter>
<div className="container">
{routes}
</div>
</BrowserRouter>
</AuthContext.Provider>
);
}
export default App;
One issue that may be causing this is you check authentication based on whether the token exists. Debug the output of that variable to see if it is cleared correctly when you log out. Next, you are determining authentication once inside of App(), fetching the routes from useRoutes(), and then rendering the app without ever checking again if the authentication is still valid. A better approach would be something like this:
const auth = useAuth();
return ({auth.isAuthenticated ? (
<Fragment>
<Link to="/account">Account ({auth.user.email})</Link>
<Button onClick={() => auth.signout()}>Signout</Button>
</Fragment>
) : (
<Link to="/signin">Signin</Link>
)});

React-Firebase routing and authentication approach

I want to learn the correct approach for firebase routing authetication,what I mean is:
function App() {
const auth = getAuth();
if (!auth) {
<Spinner />;
}
return (
<>
{auth && (
<Router>
<Routes>
<Route path='/' element={<PrivateRoute />}>
<Route index element={<Explore />} />
<Route path='/offer' element={<Offers />} />
<Route path='/profile' element={<Profile />} />
</Route>
<Route path='/sign-in' element={<SignIn />} />
<Route path='/sign-up' element={<SignUp />} />
<Route path='/forgot-password' element={<ForgotPassword />} />
</Routes>
<Navbar />
</Router>
)}
I have this code-block, at first I thought I should've gotten a useAuth hook which use onAuthStateChanged, but I realize that this auth variable from getAuth is kinda work the same way so why not use it instead of a hook?
and my PrivateRoute looks like this:
function PrivateRoute() {
const currentUser = getAuth().currentUser;
return currentUser ? <Outlet /> : <Navigate to='/sign-in' />;
}
the problem is once app mounts, because of there is no app-level state, it stays the same.
Then if I try to log off and put some logic into routing like if user exists, dont' allow routing to signup or signin, it doesn't work.
If I use redux or context API, I would dispatch whenever I login, logout, signup but without them what is the correct set-up for handling this kind of routing?
after some time, i just figured out how to do what I needed to do, so I post it here just in case someone also encounters this problem and seeks help.
If you don't use redux or context api and still want to implement this kind of feature, here's how I do:
So what I needed to do? I want authenticate through firebase, what you need to do -at least what I found- implement a APP/LEVEL/STATE in order to check if user logged in or not and update the app level rendering, that renders the whole app and enforces behaviour accordingly.
So:
function App() {
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
setIsLoading(true);
if (user) {
setUser(user);
} else {
setUser(null);
}
setIsLoading(false);
});
return unsubscribe;
}, []);
if (isLoading) return <Spinner />;
return (
<>
<Router>
<Routes>
<Route path='/' element={<PrivateRoute user={user} />}>
<Route index element={<Explore />} />
<Route path='/offers' element={<Offers />} />
<Route path='/profile' element={<Profile />} />
<Route path='/create-listing' element={<CreateListing />} />
<Route path='/category/:categoryName' element={<Category />} />
</Route>
<Route
path='/sign-in'
element={!user ? <SignIn /> : <Navigate to='/' />}
/>
<Route
path='/sign-up'
element={!user ? <SignUp /> : <Navigate to='/' />}
/>
<Route
path='/forgot-password'
element={!user ? <ForgotPassword /> : <Navigate to='/' />}
/>
</Routes>
<Navbar />
</Router>
<ToastContainer autoClose={1000} />
</>
);
}
export default App;
Here, basically after component mounts, we want useEffect to only execute once and set up listener for auth state changing, whenever auth state changes like sign-in or log-out, the code block of onAuthStateChanged runs and when it runs, every time it sets the user if there's one or not, therefore re-renders the component which is App itself, and below the routing works accordingly. If you have a private route as I do, you can just pass the user as a prop, then:
import { Navigate, Outlet } from 'react-router-dom';
function PrivateRoute({ user }) {
return user ? <Outlet /> : <Navigate to='/sign-in' />;
}
export default PrivateRoute;

Routes not working as desired in React JS

I am trying to build a full stack application with User login/logout functionality.
I want to protect certain pages such that they can only be viewed when the user is logged in. For login I have created a REST API and I am using session storage to keep track of whether the user is logged in or not.
validateUser = () => {
let user = {
username: this.state.email,
password: this.state.password,
//status: "LOGGED_IN"
};
UserService.authenticateUser(user).then((res) => {
if(res.data === 'SUCCESS') {
window.sessionStorage.setItem("isUserLogged", true);
} else if(res.data === 'FAILURE') {
window.sessionStorage.setItem("isUserLogged", false);
this.resetLoginForm();
this.setState({"error":"Invalid username or password"});
}
})
};
Tis is my App.js
function App() {
return (
<div>
<Router>
<HeaderComponent/>
<div className="container">
<Switch>
<Route path="/" exact component={LandingPageComponent}></Route>
{/* <Route path ="/customers" component = {ListCustomerComponent}></Route> */}
{/* <Route path ="/add-customer/:id" component = {CreateCustomerComponent}></Route> */}
<Route path = "/view-customer/:id" component = {ViewCustomerComponent}></Route>
<Route path = "/admin-login" component = {AdminLoginComponent}></Route>
<Route path = "/admin-register" component = {AdminResgisterComponent}></Route>
<Route path="/customers" exact render={() => (
window.sessionStorage.getItem("isUserLogged") === "true"
? <ListCustomerComponent />
: <Redirect to='/admin-login' />
)} />
<Route path="/add-customer/:id" exact render={() => (
window.sessionStorage.getItem("isUserLogged") === "true"
? <CreateCustomerComponent />
: <Redirect to='/admin-login' />
)} />
</Switch>
</div>
<FooterComponent/>
</Router>
</div>
);
}
export default App;
Everything works fine if I don't check my session storage. But when I try to implement the conditional routes as shown above I start getting errors.
If I just put simple routes, then I don't encounter this error.
Any help would be highly appreciated.
You didn't pass Route props into your component. So history does not included in props, you can console.log(this.props) to check what this.props contains.
To fix it, let's pass Route props into your components
<Route path="/add-customer/:id" exact render={(props) => (
window.sessionStorage.getItem("isUserLogged") === "true"
? <CreateCustomerComponent {...props} /> // ADD PROPS HERE
: <Redirect to='/admin-login' />
} />
You didn't show what you did on ListCustomerComponent.
You could try to encapsulate your component using HOC withRouter or if you are using Functional component, use useHistory hook.
// on export class component
export default withRouter(YourComponent)
in functional component, you can use
const YourComponent = ()=>{
const history = useHistory();
// then you can say something such as
// history.push(...)
return <>...your view here...</>
}
export default YourComponent;
<Switch>
{/* Login Sections goes Here */}
<Route exact path='/' component={MainPage} />
<Route exact path='/login' component={Login} />
<Route exact path='/admin/' component={LoginAdmin} />
<Route exact path='/register' component={Register} />
{/* AdminUser ROutes goes here */}
<SuperUserDashboard>
<Route exact path='/admin/dashboard' component={Dashboardpage} />
<Route exact path='/admin/users' component={UsersAdmin} />
</SuperUserDashboard>
<Route exact path='' component={Notfound} />
</Switch>
in superuser dashboard check if user is authenticated if not redirect to admin login page else all the routes will be visible

How to redirect user to root route if the user refreshes the page on any other route in REACT ROUTER?

So, in my project I have two routes one is the root route and other is the /game route. And I always
want the user to start from the root route because the user needs to set a mandatory state variable before moving to /game route.
But if the user refreshes the page on game route I want to redirect to root route. So all I want is that the entry point to my application is always the root route.
<Router>
<Route path="/">
<Redirect to="/" />
</Route>
<Route exact path="/" render={() => <Home setLevel={setLevel} />}></Route>
<Route exact path="/game" render={() => <Game level={level} />}></Route>
</Router>
The setup above works on localhost but when I deploy my app on Netlify it doesn't work. So if the user refreshes the page on /game route, it says page not found.
Here's full code
import { useState } from "react";
import {
BrowserRouter as Router,
Link,
Redirect,
Route,
} from "react-router-dom";
const Home = ({ setLevel }) => {
return (
<>
<h1>Choose Difficulty</h1>
<ul>
<li onClick={() => setLevel("easy")}>Easy</li>
<li onClick={() => setLevel("hard")}>Hard</li>
</ul>
<Link to="/game">Play</Link>
</>
);
};
const Game = ({ level }) => {
return (
<>
<h1>Welcome to the game</h1>
<p>Your level is {level}</p>
</>
);
};
const App = () => {
const [level, setLevel] = useState(null);
return (
<Router>
<Route path="/">
<Redirect to="/" />
</Route>
<Route exact path="/" render={() => <Home setLevel={setLevel} />}></Route>
<Route exact path="/game" render={() => <Game level={level} />}></Route>
</Router>
);
};
export default App;
I haven't investigated your React set up too closely, but you may just be missing a _redirects file for your Netflify deployment: https://docs.netlify.com/routing/redirects/#syntax-for-the-redirects-file
react-router-dom is a client side router, so when Netlify loads a page using server side rendering (aka, when you refresh or visit a page for the first time, that is not a client side redirect), it cannot find the route on the server. Adding a _redirects file will instruct Netlify how to serve up your pages on initial load.
Assuming that when the user refreshes the page, state level becomes null again.
Then update your code as below:
const Game = ({ level }) => {
// add a guard right here
if (!level) {
return <Redirect to="/"/>;
}
return (
<>
<h1>Welcome to the game</h1>
<p>Your level is {level}</p>
</>
);
};
const App = () => {
const [level, setLevel] = useState(null);
return (
<Router>
{ /* remove this code
* <Route path="/">
* <Redirect to="/" />
* </Route>
*/ }
<Route exact path="/" render={() => <Home setLevel={setLevel} />}></Route>
<Route exact path="/game" render={() => <Game level={level} />}></Route>
</Router>
);
};

How to handle two menus in one react application

I am working on application where application have two parts one for public and for admin users. I completed for public side but now I am am facing issue on admin side because when I render my 2nd menu for admin public side menu is also showing . Could someone please help me how to handle just 2nd menu let suppose if user hit ( /admin/home ) then I need to show 2nd menu if user hit (/home) then I want to show Ist menu.
Admin Route
<TopMenu>
<PrivateRoute
exact
auth={auth}
path="/dashboard"
currentUser={"admin" || null}
roles={["admin"]}
component={Dashboard}
/>
</TopMenu>
Public Route
<Route exact path="/" render={(props) => <Home {...props} />} />
<Route
exact
path="/about"
render={(props) => <About {...props} />}
/>
<Route
exact
path="/contact"
render={(props) => <Contact {...props} />}
/>
create a Private Route file and check if user is authenticated if yes show that component and if not redirect to some other route
You can do it like this or implement your own way
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
export const PrivateRoute = ({
isAuthenticated,
component: Component,
...rest
}) => (
<Route {...rest} component={(props) => (
isAuthenticated ? (
<div>
<Component {...props} />
</div>
) : (
<Redirect to="/" />
)
)} />
);
export default PrivateRoute
and in the main router file you can import the Private Route and use it like this
import PublicRoute from './routers/PublicRoute';
import Appp from './components/Appp';
import PrivateRoute from './routers/PrivateRoute';
const store = configureStore();
function App() {
return (
<div className="App">
<Provider store={store}>
<Switch>
<PublicRoute path="/" component={HomePage} exact={true} />
<PrivateRoute path="/yourPrivateRoute" component={yourComponent}/>
</Switch>
</Provider>
</div>
);
}
export default App;

Categories