React Router gives a blank screen on protected Route - reactjs

So essentially, I am trying have an authentication workflow. So basically, the Home route is protected, login is not, register is not, and then I have a verifyEmail page that opens if you arent verified.
const PrivateRoute = ({component: RouteComponent,...rest}) =>{
const{currentUser}=useContext(AuthContext)
function route (){
}
return(
<Route
{...rest}
render={routeProps =>
!!currentUser && currentUser != null
?
currentUser.emailVerified ? <RouteComponent {...routeProps}/>:(<Redirect to={"/verifyEmail"}/>)
:
(<Redirect to={"/login"}/>)
}
/>
)
}
export default PrivateRoute
And then in App.js I have
function App() {
return (
<div className={'App'}>
<AuthProvider>
<Router>
<div>
<PrivateRoute path="/" component={HomePage}/>
<Route path="/verifyEmail" component={Verify}/>
<Route path="/login" component={Login}/>
<Route path="/register" component={Register}/>
</div>
</Router>
</AuthProvider>
</div>
);
}
export default App;
Current User is basically the user credentials, I am using Firebase for authentication. The problem I am having is that after logging in it just shows a blank screen, when the email is not verified instead of showing the verifyEmail page.
To elaborate more on the problem, the actual problem is that if a user is not email verified, then it routes to nowhere, and gives me a blank screen so meaning <Redirect to="/verifyEmail" doesnt work. To debug this further I decided to replace that with a Hello</> and I saw a screen with Hello. So I dont think authentication is the problem, just that it doesnt Route to the appropriate page.
Please help.

You can create a HOC (a component) that wrap your Routes and you make your validation there.
Example:
import React, { useEffect } from 'react'
import { useHistory } from 'react-router-dom';
import { useSelector } from 'react-redux';
export default function RequireAuth({ children }) {
const history = useHistory();
const auth = useSelector(state => state.auth)
useEffect(() => {
if(!auth){
history.push("/")
}
}, [auth])
return (
<>
{children}
</>
)
}
This is an example of a personal project where I use react-redux to take auth witch is only a boolean.
I use useHistory of react-router-dom to redirect to "/" in case the user isn't logged in.
And finally in my App.js:
<div>
<RequireAuth>
<Route path='/post' component={PostPage} />
</RequireAuth>
<Route path='/' exact component={LoginPage} />
</div>

Related

How can I prevent user from going back to Login route in ReactJs?

This is a private route for allowing only authenticated users to navigate to dashboard "/".
The user can still go back to "/login" after successfully authenticated, how can I prevent this using a private route?
import React from 'react'
import { Redirect, Route } from 'react-router'
import { useAuth } from '../contexts/AuthContext'
export default function PrivateRoute({component: Component, ...rest}) {
const{ currentUser } = useAuth()
return (
<Route
{...rest}
render={props =>{
return currentUser? <Component {...props} /> : <Redirect to ="/login" />
}}>
</Route>
)
}
You can save your login state in a global state, then you are going to put a condition for rendering the login route,
const globalLoginState = true // -> you are logged in
<BrowserRouter>
<Router>
{ globalLoginState ? (
<Route exact path"/" component={DummyPageComponent} />
) : <Route path="signIn" component={DummySignInComponent} />
}
</Router>
</BrowserRouter>
I think this is gonna work for you

React Routes are not functioning well over LOGOUT?

I am working on a project where i need to integrate Auth functionality, but i counter with a redirection issue. after Logout i am not landing over /auth, it still resides on /dashboard component, but if I refresh the the page it redirects to the /auth component.
Testing Scenario:
once loge In, then same time Logout, it will work fine, will take you to the /auth
once Log In, type in the url auth, it will redirect to the Dashboard same time, which is absolutely fine. but after this, if you try to logout, it will logout, but the url will not redirect to auth.
Logout Functionality
const logout = () => {
dispatch({type: actionTypes.LOGOUT})
history.push('/auth')
setUserProfile(null)
}
Logout Reducer
import { AUTH, LOGOUT, LOGIN_CODE } from '../constants/ActionTypes'
const authReducer = (state={authData: null}, action) => {
switch (action.type) {
case LOGOUT:
localStorage.removeItem("userProfile")
return {...state, authData: null}
default:
return state
}
}
export default authReducer
Routes
<switch>
<Route path="/" component={()=> <Redirect to="/dashboard" />} exact />
<PrivateRoute path="/" component={Dashboard} auth={user} exact />
<PrivateRoute path="/dashboard" component={Dashboard} auth={user} exact />
<PublicRoute path="/auth" component={Login} auth={user} restricted={true} exact />
<swtich>
PrivateRoute Code
import React from 'react';
import {Route,Redirect} from 'react-router-dom';
const PrivateRoute = ({component : Component, auth, ...rest})=>{
return(
<Route {...rest} render={props =>{
if(!auth)
return <Redirect to="/auth"/>
return <Component {...props}/>
}}/>
)
}
export default PrivateRoute;
PublicRoute Code
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
const PublicRoute = ({auth, component: Component, restricted, ...rest}) => {
return (
// restricted = false meaning public route
// restricted = true meaning restricted route
<Route {...rest} render={props => (
auth && restricted ?
<Redirect to="/" />
: <Component {...props} />
)} />
);
};
export default PublicRoute;
import Router from 'next/router'
Then after logout send:
Router.reload();
You must Call function localStorage when you logout:
localStorage.clear();
it will redirect you into your auth page.
I searched every possible solution to fix this uncertain behavior of react-router-dom, but did not find any logical solution for this. but now at the end I came up with the hot fix for this type of issue. just need to add the tiny one line snippet in logout functionality. which I mentioned below for best reference.
reload route after logOut
window.location.reload()
this function will not hold the component and url too. once logout functionality run, will redirect to the auth route right away.

Issue updating state to reflect that the user is logged in. Navigating to '/home/ redirects me back to the login page even though state shows as true

I have the initial login configured where a user is able to login and get redirected to home, however, I'm running into an issue updating the state using { useEffect }. The state [isAuthenticated] appears to be updating correctly after viewing the console (see screenshot below), however, when I try to navigate to '/home' the protected route restricts me for some reason.
Console.log
False represents the initial state of [ isAuthenticated ] and True represents the state after { useEffect } runs.
App.js
import "./App.css";
import Login from "./Components/Login";
import Home from "./Components/Home";
import ProtectedRoute from "./Components/ProtectedRoute";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import React, { useState, useEffect } from "react";
import axios from "axios";
function App() {
const [isAuthenticated, setIsAuthenticated] = useState(false);
useEffect(() => {
async function setLoginStatus() {
try {
await axios.post("/checkIfSessionExists").then((response) => {
if (response.status === 200) {
setIsAuthenticated(true);
console.log(isAuthenticated)
} else {
setIsAuthenticated(false);
}
});
} catch (error) {
console.log(error)
}
}
setLoginStatus();
}, [isAuthenticated]);
return (
<div className="App">
<Router>
<Switch>
<Login
exact
path="/"
component={Login}
setIsAuthenticated={setIsAuthenticated}
isAuthenticated={isAuthenticated}
/>
<ProtectedRoute
exact
path="/home"
component={Home}
isAuthenticated={isAuthenticated}
/>
</Switch>
</Router>
</div>
);
}
export default App;
ProtectedRoute.js
import { Route, Redirect } from "react-router-dom";
import axios from "axios";
import { Component, useState } from "react";
const ProtectedRoute = ({ isAuthenticated, component: Component, ...rest }) => (
<Route
{...rest}
render={(props) =>
isAuthenticated === true ? (
<Component {...props} />
) : (
<Redirect to="/" />
)
}
/>
);
export default ProtectedRoute;
Here are the steps of what happens which leads your code to not work the way you expected:
initially isAuthenticated is false
so the user will be routed to the Login component no matter what url they input
after the component was rendered, useEffect runs. you send a request and then set isAuthenticated to true
now the component rerenders, but the user stays in the Login component
The problem is that you need to reroute any users that are authenticated in to a different route (in your case, Home) after they were in the Login component to authenticate
The following example works and will do the following:
if the user is authenticated:
the user can navigate to the /home route
if they go to a different route, it will redirect them to /home
if the user is NOT authenticated:
the user can navigate to the / route
if they go to a different route, it will redirect them to /
I am using <>...</> which is equivalent to <React.Fragment>, which allows you to include adjacent elements in it, which is otherwise not allowed
function App() {
const [isAuthenticated, setIsAuthenticated] = useState(false);
...
return (
<div className="App">
<Router>
<Switch>
{isAuthenticated
?
// the user is autenticated
<>
<Route path="/home" component={Home} />
<Redirect to="/home" />
</>
:
// the user is not autenticated
<>
<Route path="/" component={Login} />
<Redirect to="/" />
</>
}
</Switch>
</Router>
</div>
);
}

Using React Router, how do I redirect to a route when there is a parameter in the URL?

I have a React app using react-router-dom and I'd like to create a <Redirect /> in my top level App component if a condition is met.
import { BrowserRouter, Redirect, Route } from 'react-router-dom';
function App() {
const [user, setUser] = useState(null);
function noUserRedirect() {
// redirect to Home if user is empty
if(!user) {
return (
<Redirect to="/" />
);
}
}
return (
<BrowserRouter>
{noUserRedirect()}
<Route exact path="/">
<Home />
</Route>
<Route path="/dashboard/:id">
<Dashboard />
</Route>
</BrowserRouter>
);
}
export default App;
I know that user is not set. When I visit /dashboard, the redirect works and I am brought to the <Home /> component, but when I visit /dashboard/123, for example, the redirect function runs, but I am still brought to the <Dashboard /> component.
How can I prevent that from happening?
Why dont you just redirect programatically, like so:
import { BrowserRouter, Redirect, Route } from 'react-router-dom';
function App() {
const [user, setUser] = useState(null);
const history = useHistory();
useEffect(()=>{
if(!user)
history.push("/");
}, [user]);
return (
<BrowserRouter>
<Route exact path="/">
<Home />
</Route>
<Route path="/dashboard/:id">
<Dashboard />
</Route>
</BrowserRouter>
);
}
export default App;
It was always working. The reason I didn't think it was working was because I was getting an error in my <Dashboard /> component.
I was referencing user when it didn't exist, but I was confused because I figured the <Redirect /> would happen before getting to <Dashboard />. That's not the case.
I fixed my error in <Dashboard /> by putting this at the top of my Dashboard function:
// prevent rendering if user does not exist
if(!props.user) {
return false;
}
Once, the Dashboard component returned false, the redirect was successful.
I guess the components are rendered before the router makes the redirect. It's not super intuitive but now that I know that, it all makes sense.

Private Route on refresh is including the Redirect Route

I am trying to do a simple PrivateRoute in ReactJS and originally when I wrote the PrivateRoute function it would not redirect to the component it was set to go to.
This is the original PrivateRoute component
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
const PrivateRoute = ({component: Component, authenticated , ...rest}) => {
return (
<Route {...rest} render={(props) => (
authenticated === true
? <Component {...props} />
: <Redirect to='/login' />
)}/>
)
}
export default PrivateRoute;
I then edited the PrivateRoute Component to get rid of the {...rest} prop, and it worked fine, it redirected to the protected page. But whenever I edit the PrivateRoute Components and the browser refreshes they include both the page I edited and the Redirected Page as if I were logged out and logged in at the same time. In Hindsight the /login page I redirect if authenticated is not true would be on the top page and the Protected component would be right below it on the same page.
Here is the component that works before any refresh of the new page
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
const PrivateRoute = ({component: Component, authenticated ,}) =>
{
return (
<Route render={(props) => (
authenticated === true
? <Component {...props} />
: <Redirect to='/login' />
)}/>
)
}
export default PrivateRoute;
any help would be greatly appreciated and here is my App.js
<BrowserRouter>
<Route path="/" component = {LandingPage} exact/>
<Route path="/login" component={Login} exact/>
<Route path="/register" component={Register} exact/>
<PrivateRoute path="/dash" component={Dash} authenticated={authenticated}/>
</BrowserRouter>
I found out why I had the problem that I did in App.js file I set the authenticated state to false and did not set a timeout function which solved my problem

Resources