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.
Related
My React application, upon loading, checks to see if there's an authenticated user. If there is, then the authenticated user is redirected to their "Dashboard" page. When this happens, however, the application with briefly flicker/flash the non-authenticated landing page before the redirect. Is there a way to prevent this from happening?
import { BrowserRouter as Router, Route, Redirect } from 'react-router-dom'
import { SignUp } from './SignUp'
import { Login } from './Login'
import { Dashboard } from './Dashboard'
import { Welcome } from './Welcome.js'
import { AuthContextProvider, useAuthState } from './firebase'
const AuthenticatedRoute = ({ component: C, ...props }) => {
const { isAuthenticated } = useAuthState()
console.log(`AuthenticatedRoute: ${isAuthenticated}`)
return (
<Route
{...props}
render={routeProps =>
isAuthenticated ? <C {...routeProps} /> : <Redirect to="/welcome" />
}
/>
)
}
const UnauthenticatedRoute = ({ component: C, ...props }) => {
const { isAuthenticated } = useAuthState()
console.log(`UnauthenticatedRoute: ${isAuthenticated}`)
return (
<Route
{...props}
render={routeProps =>
!isAuthenticated ? <C {...routeProps} /> : <Redirect to="/" />
}
/>
)
}
function App() {
return (
<AuthContextProvider>
<Router>
<div></div>
<AuthenticatedRoute exact path="/" component={Dashboard} />
<UnauthenticatedRoute exact path="/signup" component={SignUp} />
<UnauthenticatedRoute exact path="/signin" component={Login} />
<UnauthenticatedRoute exact path="/welcome" component={Welcome} />
</Router>
</AuthContextProvider>
)
}
export default App
This is because while the firebase request is getting a response isAuthenticated is false.
You could use useAuthState isLoading flag to return null or an empty div before your actual return statement, so the first page loaded is blank.
if (isLoading) return null
// Then your actual return statement.
When isLoading turns false the isAuthenticated flag will resolve directly to the definitive value.
Try it and tell me if it works.
Daniel's answer explains the problem very well -- I have a few things to add though. Here is how you can get the loading status.
const [user, loading] = useAuthState(auth)
Then you can check for the loading status and return null. Hover this can create a white flash, because you're loading blank page for a second. I recommend loading the Navbar / header or footer instead of null.
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
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>
);
}
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>
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