I am using aws-amplify, react-hook in my project. The app have some private Routes has been define below:
const ProtectedRoute = ({render: C, props: childProps, ...rest}) => {
return (
<Route
{...rest}
render={rProps =>
(childProps) ? (
<C {...rProps} {...childProps} />
) : (
<Redirect
to={`/login?redirect=${rProps.location.pathname}${
rProps.location.search
}`}
/>
)
}
/>
);
}
In App.js, we change childProps to define whether user is login or not. But when childProps change, Switch not re rendering. What is the way to force React re rendering its Route because isAuthenticated is change but ProtectedRoute is not rerender.
const [isAuthenticated, userHasAuthenticated] = useState(null);
useEffect(() => {
onLoad();
}, []);
async function onLoad() {
try {
let user = await Auth.currentSession();
if (user.accessToken.payload) {
userHasAuthenticated(user.accessToken.payload);
}
} catch (e) {
if (e !== 'No current user') {
alert(e);
}
}
}
.....
const childProps = isAuthenticated;
return (
<ApolloProvider client={client} >
<div className="App">
<BrowserRouter>
<Route path='/'>
<div>
<Switch>
<Route path='/login' render={props => <Login {...props}/>} exact/>
<ProtectedRoute
exact
path='/admin/:name'
render={()=> <Admin />}
props={childProps}
/>
<Route path='/' render={props => <User {...props} />}/>
</Switch>
</div>
</Route>
</BrowserRouter>
</div>
</ApolloProvider>)
The route only renders again when you enter that URL again. You are doing a Redirect, meaning it will never have a chance to enter the same URL after authentication is complete. You should delay rendering the protected route until you have confirmed authentication:
useEffect(() => {
async function onLoad() {
try {
let user = await Auth.currentSession();
userHasAuthenticated(!!user.accessToken.payload);
} catch (e) {
if (e !== 'No current user') {
alert(e);
}
}
}
onLoad();
}, []);
...
const ProtectedRoute = ({render: C, props: childProps, ...rest}) => {
if (childProps === null) {
// app still waiting authentication
return 'Loading...';
}
return (
<Route
{...rest}
render={rProps =>
(childProps) ? (
<C {...rProps} {...childProps} />
) : (
<Redirect
to={`/login?redirect=${rProps.location.pathname}${
rProps.location.search
}`}
/>
)
}
/>
);
}
Related
In the react hooks web, I have a component called NominatePerson, in that component if the props.role is not admin it should display text Nomination View and if its admin then it should display Dashboard. This is not happening, it always displays Dashboard. Could someone help me to resolve the issue ?
App.js
function App() {
const [role, setRole] = useState();
useEffect(() => {
const fetchData = async () => {
try {
const userEmail = localStorage.getItem("loginEmail");
const res = await Axios.get(
"http://localhost:8000/service/managenomineeaccess",
{ params: { userEmail } }
);
console.log(res.data[0][0].access, "rest.data");
const data = res.data[0][0].access;
setRole(data);
} catch (e) {
console.log(e);
}
};
fetchData();
}, []);
const switchAdmin = (
<Switch>
<Route exact path='/' component={() => <Login role={role} />} />
<ProtectedRoute exact path='/dashboard' component={DashboardLayout} />
<ProtectedRoute exact path='/manageNominees' component={ManageNominees} />
<Route path='/nominatePerson' exact component={NominatePerson} />
<Route path='/nominationView' exact component={NominationView} />
</Switch>
);
const switchUser = (
<Switch>
<Route exact path='/' component={Login} />
<Route
path='/nominatePerson'
exact
component={() => <NominatePerson role={role} />}
/>
<Route
path='/nominationView'
exact
component={() => <NominationView role={role} />}
/>
<Route component={NominationView} />
</Switch>
);
return (
<Router>
<div>
<ThemeProvider theme={theme}>
<GlobalStyles />
{role === "admin" ? switchAdmin : switchUser}
</ThemeProvider>
</div>
</Router>
);
}
export default App;
NominatePerson
const NominatePerson = (props) => {
return (
<div className='leftNavItem'>
<a>
<Link
to={props.role ? "/nominationView" : "/dashboard"}
className='nav-link'
>
<b>{props.role ? "Nomination View" : "Dashboard"}</b>
</Link>
</a>
</div>
)
}
server.js // get service
app.get("/service/managenomineeaccess", async (req, res) => {
try {
let userEmail = req.query.userEmail;
let data = await sequelize.query(
`SELECT access FROM devchoice.managenominees where email="${userEmail}";`
);
res.status(200).send(data);
} catch (e) {
res.status(500).json({ fail: e.message });
}
});
This question already has an answer here:
React-Router-Dom unable to render page but routes back due to PrivateRoute
(1 answer)
Closed 8 months ago.
My problem is that the moment i navigate to the homepage and the user is not authenticated the page shows for a split second and then move on to the login page. I want it to redirect to login only and not show the homepage for a split second
I already created a private route in my project but for a split second the protected routes shows when i navigate on to it.
here is my code:
AuthContextProvider
const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
setUser(currentUser);
});
return () => unsubscribe();
}, []);
const value = { user };
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
};
PrivateRoute.js
const PrivateRoute = ({ children }) => {
let { user } = useAuth();
if (user) {
return <Outlet />;
} else {
return <Navigate to="/login" />;
}
};
App.js
function App() {
return (
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route path="/" element={<Layout />}>
<Route element={<PrivateRoute />}>
<Route path="/" element={<Home />} />
<Route path="/home" element={<Home />} />
<Route path="/reminders" element={<Reminders />} />
<Route path="/archive" element={<Archive />} />
<Route path="/trash" element={<Trash />} />
</Route>
</Route>
</Routes>
);
}
Loginpage.js
const LoginPage = () => {
const { user } = useAuth();
const navigate = useNavigate();
const signIn = async () => {
const provider = new GoogleAuthProvider();
await signInWithRedirect(auth, provider);
};
useEffect(() => {
onAuthStateChanged(auth, (currentUser) => {
if (currentUser) {
navigate("/");
}
});
}, []);
return (
<>
<button
onClick={signIn}
className="bg-blue-500 p-3 text-white hover:bg-blue-600"
>
Sign In With Google
</button>
</>
);
};
Code for hindering the Loggedin User to go to the Login Route
I have created a SpecialRoute which will redirect the loggedIn User to the mainpage if the user tries to go the login page.
SpecialRoute.js
import { Login } from '../pages/Login';
import { useAuth } from '../firebase-config';
import React from 'react';
import { Outlet, Navigate } from 'react-router-dom';
// import { useAuth } from '../firebase-config';
export const SpecialRoute = () => {
const user = useAuth();
return user ? <Outlet /> : <Navigate to="/" replace />;
};
App.js
<Route element={<SpecialRoute />}>
<Route path="/login" element={<Login />} />
</Route>
In your Private Route Component, do this :-
const PrivateRoute = ({ children }) => {
let { user } = useAuth();
return typeof user === 'undefined' ? (
<h1>Loading.....</h1>
) : user ? (
<Outlet />
) : (
<Navigate to="/login" />
);
};
Below is how I created my private routes (in Firebase v9):-
useAuth Hook
// custom hook
export function useAuth() {
//
const [currentUser, setCurrentUser] = useState<any>();
useEffect(() => {
const unSubscribe = onAuthStateChanged(auth, (user) =>
setCurrentUser(user)
);
return unSubscribe;
}, []);
return currentUser;
}
My PrivateRoute/ProtectedRouted Component
import { Login } from '../pages/Login';
import React from 'react';
import { Outlet } from 'react-router-dom';
import { useAuth } from '../firebase-config';
export const ProtectedRoute = () => {
const user = useAuth();
console.log('/////user autheticated', user);
return typeof user === 'undefined' ? (
<h1>Loading.....</h1>
) : user ? (
<Outlet />
) : (
<Login />
);
};
App.js
function App() {
return (
<>
<Router>
<Routes>
<Route element={<ProtectedRoute />}>
<Route path="/" element={<Home />} />
</Route>
<Route path="/login" element={<Login />} />
<Route path="/signup" element={<Signin />} />
</Routes>
</Router>
</>
);
}
I use this method to show private pages to the user. The problem is that no page is displayed to me, but the logs that I put in PrivatePage work properly.
Components are correct because when I simply call the routes, the components are returned correctly but it has a problem with PrivatePage and does nothing.
Error Text:
Error: Objects are not valid as a React child (found: [object
Promise]). If you meant to render a collection of children, use an
array instead.
I must verify that user through the server before the user goes to an address because the user has a time limit to use the site, so I used Promise.
function Auth() {
return new Promise((resolve, reject) => {
let appData = GetAppData();
if (appData.UserToken.length == 0) {
reject(-2);
}
let request = {
Token: appData.UserToken,
UserId: appData.UserId
}
axios.post("****UserValidUrl****", request).then(response => {
switch (response.data.Type) {
case 10:
resolve(10);
break;
case -2:
reject(-2);
break;
case -13:
reject(-13);
}
}).catch(err => {
reject(-13);
})
})
}
PrivatePage:
const PrivatePage = ({children, ...rest}) => (
return <Route
{...rest}
render={async ({location }) => {
let type = 10;
try {
type = await Auth();
} catch(e) {
type = -13;
}
Log(type);
switch (type) {
case 10:
return children;
break
case -2:
return (
<Redirect
to={{
pathname: '/Auth/Login',
state: { from: location },
}}
/>)
case -13:
return (
<Redirect
to={{
pathname: '/NotFound',
state: { from: location },
}}
/>)
break;
}}
}/>
)
BasePage:
export default function BasePage() {
return (
<div>
<BrowserRouter>
<Switch>
<Route exact path={"/Auth/Login"} component={Login}/>
<PrivatePage path={"/"}>
<Switch>
<Route exact path={"/"} component={Home}/>
<Route exact path={"/Dashboard/Home"} component={Home}/>
<Route exact path={"/Dashboard/Profile"} component={Profile}/>
<Route component={NotFound}/>
</Switch>
}/>
</Switch>
</BrowserRouter>
</div>
);
}
Your navigation looks complex for authentication. you can treat your component as route and do routing as this.
Assuming that you have authenticated separately and you just check if it's good to go, instead of trying to authenticate every single time for route. If you prefer this authentication on route, still solution applies.
function AuthenticatedRoute({ children, ...props }) {
const isAuthenticated = useContext(AuthenticationContext);
return isAuthenticated ? (
<Route {...props}>{children}</Route>
) : (
<Redirect
to={{
pathname: '/login',
state: { from: location },
}}
/>
);
}
and use it like:
<AuthenticatedRoute path="/Dashboard/Home" exact>
<h1>Hey Doctor Strange! You made it</h1>
</AuthenticatedRoute>
I also have created a protected auth routing in this code sandbox for reference:
https://codesandbox.io/s/cranky-poincare-l2c06?file=/src/App.js:262-488
Maybe you can do something like this in your private route.
PrivatePage
const PrivatePage = ({ children, ...rest }) => (
<Route
{...rest}
render={async ({ location }) => {
let isAuthenticated = false;
try {
isAuthenticated = await Auth();
} catch(e) {
// If you want to log the error.
}
if (isAuthenticated) {
return children;
}
return (
<Redirect
to={{
pathname: '/login',
state: { from: location },
}}
/>
);
}}
/>
);
BasePage
export default function BasePage() {
return (
<div>
<BrowserRouter>
<Switch>
<Route exact path={"/Auth/Login"} component={Login}/>
<PrivatePage path={"/"}>
<Switch>
<Route exact path={"/"} component={Home}/>
<Route exact path={"/Dashboard/Home"} component={Home}/>
<Route exact path={"/Dashboard/Profile"} component={Profile}/>
<Route component={NotFound}/>
</Switch>
</PrivatePage>
</Switch>
</BrowserRouter>
</div>
);
}
EDIT
PrivatePage for specific numeric value in Auth.
const PrivatePage = ({children, ...rest}) => (
return <Route
{...rest}
render={async ({location }) => {
let authenticated = false;
try {
authenticated = await Auth();
} catch(e) {
// If you want to log the error.
}
if(authenticated === -2) {
// redirect one
} else if (authenticated === -13) {
// redirect two
} if (authenticated) {
return children;
}
return (
<Redirect
to={{
pathname: "/Auth/Login",
state: { from: location },
}}
/>
);
}}
/>
)
export default function PrivateRoute({component: Component, ...props}) {
let [PAGE_LOADING, SET_PAGE_LOADING] = useState(LOADING_TYPE.Loading);
let [AUTHENTICATION, SET_AUTHENTICATE] = useState(0);
useEffect(() => {
Auth().then((resolve) => {
SET_AUTHENTICATE(resolve);
}).catch((reject) => SET_AUTHENTICATE(reject))
.then(() => SET_PAGE_LOADING(LOADING_TYPE.Default))
}, [])
return (
PAGE_LOADING === LOADING_TYPE.Loading && AUTHENTICATION == 0 ?
<CtLabel/> :
<Route
{...props}
render={props => (
AUTHENTICATION == 10 ?
<Component {...props}/> :
<Redirect to={ROUTE_PATH.LOGIN}/>
)}/>
)
}
And for Used:
export default function BasePage() {
return (
<BrowserRouter>
<Switch>
{/*Other Routes*/}
<Private path={***} component={Home} exact/>
<Private path={***} component={Profile} exact/>
{/*Other Routes*/}
<Route component={NotFound}/>
</Switch>
</BrowserRouter>
);
}
I managed to make a private route and navigate to different pages using react-router-dom. How ever, when I navigate to a page and reload it, it first goes to /login for half a second and the reloads the page correctly. How can I prevent this unwanted behavior and improve my routing?
Here are my routes:
<Router>
<Route
path="/"
component={() =>
!auth ? <Redirect to="/login" /> : <Redirect to={path} />
}
/>
<Route exact path="/home" component={Home} />
<Route exact path="/dashboard" component={Dashboard} />
<Route exact path="/login" component={RedirectPage} />
</Router>
This is the full component:
import {
Route,
BrowserRouter as Router,
Link,
Redirect,
} from "react-router-dom";
import { Container, Button } from "#material-ui/core/";
import Login from "./Login";
import { useContext,useState } from "react";
import { UserContext } from "../App";
import { signOut } from "../Storage/Auth";
const Routes = () => {
const { auth, setAuth, logging } = useContext(UserContext);
const [path,setPath] = useState("/home")
const handleSignOut = () => {
signOut(setAuth);
console.log("Auth", auth);
};
const Home = () => {
console.log("Home");
return (
<Container>
<h1>Welcome</h1>
<Link to="/">
<Button onClick={handleSignOut}> Log Out</Button>
</Link>
<Link to="/dashboard">
<Button> Dash</Button>
</Link>
</Container>
);
};
const Dashboard = () => {
setPath("/dashboard")
console.log("Dash");
return (
<Container>
<Link to="/home">
<Button> HOME</Button>
</Link>
<h1>Dashboard</h1>
</Container>
);
};
const RedirectPage = () => {
if (!logging) {
return <div></div>;
} else {
return <Login />;
}
};
return (
<Router>
<Route
path="/"
component={() =>
!auth ? <Redirect to="/login" /> : <Redirect to={path} />
}
/>
<Route exact path="/home" component={Home} />
<Route exact path="/dashboard" component={Dashboard} />
<Route exact path="/login" component={RedirectPage} />
</Router>
);
};
export { Routes };
This is my Login component.
import { useState, useContext } from "react";
import {
Button,
Card,
Container,
Typography,
Box,
TextField,
} from "#material-ui/core/";
import { useHistory} from "react-router-dom";
import { signIn } from "../Storage/Auth";
import { UserContext } from "../App";
const Login = () => {
const [mail, setMail] = useState<string>("");
const [password, setPassword] = useState<string>("");
const { user, setUser } = useContext(UserContext);
const handleSignIn = async (m: string, p: string) => {
await signIn(m, p).then((e) => {
console.log("USERID", e, user);
setUser(e);
});
};
const history = useHistory();
const handleEnter = () => {
history.push("/home");
};
const handleOnKey = (e: any) => {
if (e.key === "Enter") {
e.preventDefault();
handleSignIn(mail, password);
handleEnter();
}
};
return (
<Card className="Card" raised={true}>
<Container className="Input">
<Typography className="Sign-in" paragraph={true} variant="inherit">
Sign in
</Typography>
<Box
className="Box"
borderColor="error.main"
border={2}
borderRadius="borderRadius"
>
<Container>
<TextField
fullWidth={true}
placeholder=" email"
value={mail}
onChange={(e) => {
setMail(e.target.value);
}}
onKeyDown={(e) => {
handleOnKey(e);
}}
/>
</Container>
</Box>
</Container>
<Container className="Input">
<Box
className="Box"
borderColor="error.main"
borderRadius="borderRadius"
border={2}
>
<Container>
<TextField
fullWidth={true}
placeholder=" password"
value={password}
onChange={(e) => {
setPassword(e.target.value);
}}
type="password"
onKeyDown={(e) => {
handleOnKey(e);
}}
/>
</Container>
</Box>
<h1> </h1>
<Button
onClick={() => {
handleSignIn(mail, password);
}}
fullWidth={true}
color="primary"
variant="contained"
type="submit"
>
Sign In{" "}
</Button>
<h1> </h1>
<Box className="Sign-in">
<Button size="small"> Register </Button>
</Box>
<h1> </h1>
</Container>
</Card>
);
};
export default Login;
This is the App component:
import { useEffect } from "react";
import { Routes } from "./Routing/Routes";
import "./App.css";
import { Container } from "#material-ui/core/";
import initFirebase from "./Storage/Secret";
import { useState, createContext } from "react";
import { onAuthChange } from "./Storage/Auth";
export const UserContext = createContext<any>(null);
function App() {
const [user, setUser] = useState(null);
const [auth, setAuth] = useState<string | null>("");
const [logging, setLogging] = useState(null)
useEffect(() => {
initFirebase();
}, []);
useEffect(() => {
onAuthChange(setAuth,setLogging);
}, [auth]);
return (
<UserContext.Provider value={{ user, setUser, auth,setAuth,logging }}>
<div className="App">
<Container>
<Routes />
</Container>
</div>
</UserContext.Provider>
);
}
export default App;
Also, here is the auth logic:
import firebase from "firebase/app";
import "firebase/auth";
const auth = () => firebase.auth();
const signIn = async (email, password) => {
await auth()
.signInWithEmailAndPassword(email, password)
.then((userCredential) => {
var user = userCredential.user;
console.log("USER", user);
return user.uid;
})
.catch((error) => {
var errorCode = error.code;
var errorMessage = error.message;
alert(errorCode, errorMessage);
return null;
});
};
const onAuthChange = (setState, setLoading) => {
auth().onAuthStateChanged((u) => {
if (!u) {
console.log(u);
setLoading(true);
} else {
setState(u);
setLoading(false);
}
});
};
const signOut = (setState) => {
auth()
.signOut()
.then(function () {
console.log("LOGGED OUT");
})
.catch(function (error) {
console.log("ERROR LOGGING OUT");
});
setState(null);
};
export { signIn, signOut, onAuthChange }
Finally, the full code is in https://gitlab.com/programandoconro/adminkanjicreator
Any suggestion will be appreciated, thanks.
I would recommend doing the auth check earlier. So something like this so that the routes themselves only get rendered if there is something in auth. I think your example is also missing the Switch statement which often helps.
<Router>
{!auth ? (
<Switch>
<Route exact path="/login" component={RedirectPage} />
</Switch>
) : (
<Switch>
<Route exact path="/home" component={Home} />
<Route exact path="/dashboard" component={Dashboard} />
</Switch>
)}
</Router>
Typically you will want some sort of "loading" or "indeterminant" state to represent neither authenticated nor unauthenticated. You can use this third "state" to hold the UI before committing to rendering one way or the other on anything based upon authentication.
Since your auth logic resolves to a boolean true|false.
const onAuthChange = (setState, setLoading) => {
auth().onAuthStateChanged((u) => {
if (!u) {
console.log(u);
setLoading(true);
} else {
setState(u);
setLoading(false);
}
});
};
You can use the fact that the initial auth state is neither of these. I suggest using null.
const [auth, setAuth] = useState<string | null>(null);
When rendering the Route utilizing the auth state you can augment the logic to return early before deciding to redirect.
<Route
path="/"
render={() => {
if (auth === null) return null;
return <Redirect to={auth ? path : "/login" />;
}}
/>
Note here that I've also switched over to the render prop, the component prop is intended for attaching actual React components. These are treated a little differently. You can read about the route render method differences here.
The full router example:
<Router>
<Switch>
<Route path="/home" component={Home} />
<Route path="/dashboard" component={Dashboard} />
<Route path="/login" component={RedirectPage} />
<Route
path="/"
render={() => {
if (auth === null) return null;
return <Redirect to={auth ? path : "/login" />;
}}
/>
</Switch>
</Router>
Note here that I've also included the Switch component and reordered the routes so the more specific paths are listed before less specific paths. This allows you to remove the unnecessary exact prop from all the routes since the Switch renders routes exclusively (versus inclusively as the Router does).
I finally managed to solve the issue. Now the reload works perfectly and the security was implemented as excepted. This is my final Router:
<Router>
<Route
path="/"
render={() =>
logging ? <Redirect to={"/login"} /> : <Redirect to={path} />
}
/>
<Route exact path="/" render={() => auth && <Home />} />
<Route exact path="/dashboard" render={() => auth && <Dashboard />} />
<Route exact path="/login" component={Login} />
</Router>
This is how the component looks like now.
import {
Route,
BrowserRouter as Router,
Link,
Redirect
} from "react-router-dom";
import { Container, Button } from "#material-ui/core/";
import Login from "./Login";
import { useContext, useState, useEffect } from "react";
import { UserContext } from "../App";
import { signOut } from "../Storage/Auth";
const Routes = () => {
const { auth, setAuth, logging } = useContext(UserContext);
const handleSignOut = () => {
signOut(setAuth);
console.log("Auth", auth);
};
const pathname = window.location.pathname;
const [path, setPath] = useState(pathname);
useEffect(() => {
console.log(path);
path === "/login" && setPath("/");
path !== "/" && path !== "/dashboard" && setPath("/");
}, [auth]);
const Home = () => {
console.log("Home");
return (
<Container>
<h1>Welcome</h1>
<Link to="/">
<Button onClick={handleSignOut}> Log Out</Button>
</Link>
<Link to="/dashboard">
<Button> Dash</Button>
</Link>
</Container>
);
};
const Dashboard = () => {
console.log("Dash");
return (
<Container>
<Link to="/">
<Button> HOME</Button>
</Link>
<h1>Dashboard</h1>
</Container>
);
};
return (
<Router>
<Route
path="/"
render={() =>
logging ? <Redirect to={"/login"} /> : <Redirect to={path} />
}
/>
<Route exact path="/" render={() => auth && <Home />} />
<Route exact path="/dashboard" render={() => auth && <Dashboard />} />
<Route exact path="/login" component={Login} />
</Router>
);
};
export { Routes };
Thanks #Richard and #Drew for their kind support.
Whenever I go the the path ("/") even if I am logged in, Signup component is loaded once for few second then Dashboard is shown same happens with redirecting how to solve it.
<Route
exact
path="/"
render={() => {
return loggedIn ? <Dashboard /> : <Signup />;
}}
/>
<Route
exact
path="/resetPassword"
render={() => {
return loggedIn ? (
<Redirect to="/" />
) : (
<ResetPassword/>
);
}}
/>
and I'm setting the login like this:
const cookieCheck = () => {
const mt = Cookies.get("rt");
if (mt === "" || mt === undefined) {
if (loggedIn) {
setLogin(false);
}
} else {
if (!loggedIn) {
setLoggedIn(true);
}
}
};
My complete code is as follows
import React, { Fragment, useState, useEffect, lazy, Suspense } from 'react';
import { Paper } from '#material-ui/core';
import Dashboard from './Components/Dashboard/Dashboard';
import LoadingPage from './Components/LoadingPage/main';
import ForgotPassword from './Components/ForgotPassword/Fp';
import Signup from './Components/Signup/Signup';
import Login from './Components/Login/Login';
import PageNotFound from './Components/404/Page.js';
import Activation from './Components/Activation/Activation';
import ResetPassword from './Components/ResetPassword/Reset';
import {
BrowserRouter as Router,
Route,
Switch,
Redirect,
} from 'react-router-dom';
import Cookies from 'js-cookie';
const App = () => {
const [login, setLogin] = useState(false);
const checkStatus = function () {
const mt = Cookies.get('t');
if (mt === '' || mt === undefined) {
setLogin(false);
} else {
setLogin(true);
// return true;
}
};
useEffect(() => {
const interval = setInterval(() => {
cookieCheck();
}, 1000);
return () => clearInterval(interval);
});
const cookieCheck = async () => {
const mt = await Cookies.get('rt');
if (mt === '' || mt === undefined) {
if (login) {
setLogin(false);
}
} else {
if (!login) {
setLogin(true);
}
}
};
console.log(login);
return (
<Router>
<Fragment>
<Paper elevation={0}>
<Switch>
<Route
path="/forgotpassword"
exact
component={ForgotPassword}
/>
<Route
exact
path="/login"
render={() => {
return login ? <Redirect to="/" /> : <Login />;
}}
/>
<Route
exact
path="/signup"
render={() => {
if (login) {
return <Redirect to="/" />;
} else {
return <Signup />;
}
}}
/>
<Route
exact
path="/"
render={() => {
return login ? (
<Dashboard />
) : (
<LoadingPage />
);
}}
/>
<Route
exact
path="/resetPassword/:code/:uid"
render={() => {
return login ? (
<Redirect to="/" />
) : (
ResetPassword
);
}}
/>
<Route
exact
path="/activation/:code/:uid"
component={Activation}
/>
<Route component={PageNotFound} />
</Switch>
</Paper>
</Fragment>
</Router>
);
};
export default App;
You need try something like this to add loading state to your component:
import React, { Fragment, useState, useEffect, lazy, Suspense } from "react";
import { Paper } from "#material-ui/core";
import Dashboard from "./Components/Dashboard/Dashboard";
import LoadingPage from "./Components/LoadingPage/main";
import ForgotPassword from "./Components/ForgotPassword/Fp";
import Signup from "./Components/Signup/Signup";
import Login from "./Components/Login/Login";
import PageNotFound from "./Components/404/Page.js";
import Activation from "./Components/Activation/Activation";
import ResetPassword from "./Components/ResetPassword/Reset";
import {
BrowserRouter as Router,
Route,
Switch,
Redirect,
} from "react-router-dom";
import Cookies from "js-cookie";
const App = () => {
const [login, setLogin] = useState(false);
const [loading, setLoading] = useState(true);
const checkStatus = function () {
const mt = Cookies.get("t");
if (mt === "" || mt === undefined) {
setLogin(false);
} else {
setLogin(true);
}
setLoading(false);
};
useEffect(() => {
const interval = setInterval(() => {
checkStatus();
}, 1000);
return () => clearInterval(interval);
}, []);
console.log(login);
const isLoginComponent = login ? <Dashboard /> : <LoadingPage />;
return (
<Router>
<Fragment>
<Paper elevation={0}>
<Switch>
<Route path="/forgotpassword" exact component={ForgotPassword} />
<Route
exact
path="/login"
render={() => {
return login ? <Redirect to="/" /> : <Login />;
}}
/>
<Route
exact
path="/signup"
render={() => {
if (login) {
return <Redirect to="/" />;
} else {
return <Signup />;
}
}}
/>
<Route
exact
path="/"
render={() => {
return loading ? <div>loading</div> : isLoginComponent;
}}
/>
<Route
exact
path="/resetPassword/:code/:uid"
render={() => {
return login ? <Redirect to="/" /> : ResetPassword;
}}
/>
<Route exact path="/activation/:code/:uid" component={Activation} />
<Route component={PageNotFound} />
</Switch>
</Paper>
</Fragment>
</Router>
);
};
export default App;
Read more about how cookies work here: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/cookies/get
I think this issue is you don't immediately check the cookie status when the component mounts. This coupled with login initial state false will render any login ? true : false false branch component until the first cookie check that returns valid authentication.
const [login, setLogin] = useState(false);
const checkStatus = function () {
const mt = Cookies.get('t');
if (mt === '' || mt === undefined) {
setLogin(false);
} else {
setLogin(true);
}
};
useEffect(() => {
const interval = setInterval(() => {
checkStatus(); // <-- not called for 1000ms
}, 1000);
return () => clearInterval(interval);
});
Solution
Need to also immediately invoke the cookie check function. I suggest also starting with a non-true/false state value so you know when the initial check is complete.
const [login, setLogin] = useState(null);
const checkStatus = function () {
const mt = Cookies.get('t');
if (mt === '' || mt === undefined) {
setLogin(false);
} else {
setLogin(true);
}
};
useEffect(() => {
const interval = setInterval(() => {
checkStatus();
}, 1000);
checkStatus(); // <-- also invoke first render
return () => clearInterval(interval);
});
Check for login === null and return null (or any pending/loading UI really), otherwise return the regular ternary.
<Route
exact
path="/"
render={() => {
if (login === null) return null;
return login ? (
<Dashboard />
) : (
<LoadingPage />
);
}}
/>