How do you wait for useAuth useEffect to return the current user state? - javascript

I have a bit of a problem implementing authentication for my React application. I followed this link to get the authentication going. Here's my App component:
function App() {
return (
<ProvideAuth>
<BrowserRouter>
<Header />
<Switch>
<PrivateRoute exact path="/">
<Dashboard />
</PrivateRoute>
<Route path="/login">
<Login />
</Route>
</Switch>
</BrowserRouter>
</ProvideAuth>
);
}
function PrivateRoute({ children, ...rest }) {
let auth = useAuth();
console.log("USER: ", auth.user);
return (
<Route
{...rest}
render={({ location }) =>
auth.user ? (
children
) : (
<Redirect
to={{
pathname: "/login",
state: { from: location }
}}
/>
)} />
)
}
export default App;
Login component:
const Login = () => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
let history = useHistory();
let location = useLocation();
let auth = useAuth();
let { from } = location.state || { from: { pathname: "/" } }
let login = (e) => {
auth.signin(email, password, () => {
history.replace(from);
});
};
return (
<div>
<input onChange={e => setEmail(e.target.value)} value={email} type="email" />
<input onChange={e => setPassword(e.target.value)} value={password} type="password" />
</div>
)
}
export default Login;
Finally use-auth.js:
const authContext = createContext();
export function ProvideAuth({ children }) {
const auth = useProvideAuth();
return <authContext.Provider value={auth}>{children}</authContext.Provider>;
};
export const useAuth = () => {
return useContext(authContext);
};
function useProvideAuth() {
const [user, setUser] = useState(null);
const signin = (email, password, callback) => {
axios.post(`${apiUrl}/sign_in`, {
'email': email,
'password': password
},
{
headers: {
'Content-Type': 'application/json'
}
}).then(res => {
const expiryDate = new Date(new Date().getTime() + 6 * 60 * 60 * 1000).toUTCString();
document.cookie = `access-token=${res.headers['access-token']}; path=/; expires=${expiryDate}; secure; samesite=lax`;
return res.data
})
.then(data => {
setUser(data.data);
callback();
})
.catch(e => {
setUser(null);
});
};
const signout = () => {
document.cookie = "access-token=; expires = Thu, 01 Jan 1970 00:00:00 GMT";
setUser(null);
}
useEffect(() => {
const cookies = getCookies();
if (cookies['access-token']) {
axios.get(`${apiUrl}/user_info`, {
headers: {
...cookies
}
}).then(res => {
return res.data;
})
.then(data => {
setUser(data);
})
.catch(e => {
setUser(null);
})
} else {
setUser(null);
}
}, []);
return {
user,
signin,
signout
}
}
function getCookies() {
let cookies = document.cookie.split(';');
let authTokens = {
'access-token': null
};
for (const cookie of cookies) {
let cookiePair = cookie.split('=');
if (authTokens.hasOwnProperty(cookiePair[0].trim().toLowerCase()))
authTokens[cookiePair[0].trim()] = decodeURIComponent(cookiePair[1]);
}
return authTokens;
}
and then the dashboard component is the homepage. Nothing interesting.
The problem is when a user is in fact logged in (the access-token cookie is set as well as other tokens), they're still routed to the login page because of the fact that calling the API which checks that these tokens are valid is asynchronous, so the user is set to null initially.
What am I missing here? how can I wait until the API response is returned without blocking the user interface? Should I save user state in the redux state or is there some other work around?
Thanks a lot!

Like Jonas Wilms suggested, I added a loading state variable in user-auth similar to user and set it to true before each request and false after the request is completed.
In my App component, I changed the PrivateRoute function to show a loading spinner as long as the user state is loading. When it's set to false, I check whether the user is logged in or not and show the Dashboard component or redirect to login page accordingly.
function PrivateRoute({ children, ...rest }) {
let auth = useAuth();
return (
<Route
{...rest}
render={({ location }) =>
auth.loading ?
<Loading /> :
auth.user ? (
children
) : (
<Redirect
to={{
pathname: "/login",
state: { from: location }
}}
/>
)} />
)
}

Related

React context provider updates state after context consumer renders

I am trying to implement Protected Routes in my app. I am using cookie-based session authentication.
The issue is: Whenever I try to access a protected page for the first time, the RequireAuth component has the isAuthenticated value as false and hence it navigates to /.
From the console logs, I can see Inside require auth. before Inside provide auth..
Questions:
Is using useEffect in the context provider the right way to set the auth state?
How do I make sure that the context provider state is set before accessing the context in the consumer RequireAuth?
I have a context provider ProvideAuth which makes an API call to check if the user is already authenticated.
const authContext = createContext();
export function ProvideAuth({ children }) {
const navigate = useNavigate();
const location = useLocation();
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [userInfo, setUserInfo] = useState({});
const fetchData = async () => {
const isAuthenticated = await CheckAuthentication();
setIsAuthenticated(isAuthenticated);
if (isAuthenticated) {
const userInfo = await GetUserInfo();
setUserInfo(userInfo);
}
}
useEffect(() => {
console.log("Inside provide auth. " + isAuthenticated + " " + location.pathname);
fetchData();
}, []);
const value = {
isAuthenticated,
userInfo
};
return <authContext.Provider value={value}>{children}</authContext.Provider>;
}
Auth context consumer
export const useAuth = () => {
return useContext(authContext);
};
I use the context in a RequireAuth component to check if the user is already authenticated and redirect if not.
export default function RequireAuth({ children }) {
const { isAuthenticated, userInfo } = useAuth();
const location = useLocation();
useEffect(() => {
console.log("Inside require auth. " + isAuthenticated + " " + location.pathname);
}, []);
return isAuthenticated === true ?
(children ? children : <Outlet />) :
<Navigate to="/" replace state={{ from: location }} />;
}
The context provider is used in the App.js
return (
<ProvideAuth>
<div className='App'>
<Routes>
<Route exact path="/" element={<Home />} />
<Route path="/pricing" element={<Pricing />} />
<Route element={<RequireAuth /> }>
<Route path="/jobs" element={<Jobs />} >
<Route index element={<MyJobs />} />
<Route path="new" element={<NewJob />} />
<Route path=":jobId" element={<JobDetails />} />
<Route path=":jobId/stats" element={<JobStats />} />
</Route>
</Route>
<Route path="*" element={<NotFound />} />
</Routes>
</div>
</ProvideAuth>
);
That's because that useEffect in ProvideAuth is as any useEffect an asynchronous task, which means the component and its children may render before its callback gets executed.
A solution is to set up a loading state in ProvideAuth, called for example isCheckingAuth, set to true by default, and to false after you have done all the fetching. And you pass it down to RequireAuth, like so :
const authContext = createContext();
export function ProvideAuth({ children }) {
const navigate = useNavigate();
const location = useLocation();
const [isCheckingAuth, setIsCheckingAuth] = useState(true);
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [userInfo, setUserInfo] = useState({});
const fetchData = async () => {
const isAuthenticated = await CheckAuthentication();
setIsAuthenticated(isAuthenticated);
if (isAuthenticated) {
const userInfo = await GetUserInfo();
setUserInfo(userInfo);
}
setIsCheckingAuth(false)
}
useEffect(() => {
console.log("Inside provide auth. " + isAuthenticated + " " + location.pathname);
fetchData();
}, []);
const value = {
isAuthenticated,
userInfo,
isCheckingAuth
};
return <authContext.Provider value={value}>{children}</authContext.Provider>;
}
You use that isCheckingAuth inRequireAuth to show a loader while the fetching is being done, this way:
export default function RequireAuth({ children }) {
const { isAuthenticated, userInfo, isCheckingAuth } = useAuth();
const location = useLocation();
useEffect(() => {
if(isCheckingAuth) return;
console.log("Inside require auth. " + isAuthenticated + " " + location.pathname);
}, [isCheckingAuth]);
if(isCheckingAuth) return <div>Loading...</div>
return isAuthenticated === true ?
(children ? children : <Outlet />) :
<Navigate to="/" replace state={{ from: location }} />;
}
What you can do is check, If the request is processed or not. If processing show loader if any error shows some error msg or redirect. If everything is fine load provider.
const authContext = createContext();
export function ProvideAuth({ children }) {
const [state, setState] = useState({
user: null,
isAuthenticated: false,
isLoading: false,
error: null,
});
useEffect(() => {
const fetchData = async () => {
try {
const isAuthenticated = await CheckAuthentication();
if (isAuthenticated) {
const user = await GetUserInfo();
setState((prev) => ({ ...prev, isAuthenticated, user }));
}
} catch (error) {
setState((prev) => ({ ...prev, error }));
} finally {
setState((prev) => ({ ...prev, isLoading: false }));
}
};
fetchData();
}, []);
if (state.isLoading) return <Loading />;
if (state.error) return <ErrorMessage error={state.error} />;
return <authContext.Provider value={state}>{children}</authContext.Provider>;
}

React - How to stay on the same page even if it was refreshed?

I'm using react-router for the link to the different pages. Everything works fine, however, once I'll refresh the page, it'll go to the login page for a moment and it'll go back to the homepage. It was even worse if I'll go to the admin page, refreshing the page will direct the user to the login page, however, the user is still logged in and only displays the login page. I'm also using Firebase Firestore and firebase authentication.
app.js
const App = (props) => {
const { setCurrentUser, currentUser } = props;
const admin = checkUserAdmin(currentUser);
console.log(admin);
useEffect(() => {
const authListener = auth.onAuthStateChanged(async (userAuth) => {
if (userAuth) {
const userRef = await handleUserProfile(userAuth);
userRef.onSnapshot((snapshot) => {
setCurrentUser({
id: snapshot.id,
...snapshot.data(),
});
});
}
setCurrentUser(userAuth);
});
return () => {
authListener();
};
}, []);
return (
<div className="App">
<Switch>
<Route
exact
path="/login"
render={() => (
<MainLayout>
<LoginPage />
</MainLayout>
)}
/>
<Route
exact
path="/profile"
render={() => (
<WithAuth>
<MainLayout>
<ProfilePage />
</MainLayout>
</WithAuth>
)}
/>
<Route
exact
path="/admin"
render={() => (
<WithAdmin>
<AdminHome />
</WithAdmin>
)}
/>
</Switch>
</div>
);
};
const mapStateToProps = ({ user }) => ({
currentUser: user.currentUser,
});
const mapDispatchToProps = (dispatch) => ({
setCurrentUser: (user) => dispatch(setCurrentUser(user)),
});
export default connect(mapStateToProps, mapDispatchToProps)(App);
withAuth - restricting the users for the pages. If currentUser is a guest user, it directs the user to the login page.
import { useAuth } from "./../custom-hooks";
import { withRouter } from "react-router-dom";
const WithAuth = (props) => useAuth(props) && props.children;
export default withRouter(WithAuth);
useAuth - restricting the users for the pages. If currentUser is a guest user, it directs the user to the login page.
const mapState = ({ user }) => ({
currentUser: user.currentUser,
});
const useAuth = (props) => {
const { currentUser } = useSelector(mapState);
useEffect(() => {
if (!currentUser) {
props.history.push("/login");
}
}, [currentUser]);
return currentUser;
};
export default useAuth;
withAdmin - pages only accessible to the admin
import { useAdmin } from "../../custom-hooks";
const WithAdmin = (props) => useAdmin(props) && props.children;
export default WithAdmin;
useAdmin - pages only accessible to the admin. If user is not an admin, it directs the user to the login page.
const mapState = ({ user }) => ({
currentUser: user.currentUser,
});
const useAdmin = (props) => {
const { currentUser } = useSelector(mapState);
const history = useHistory();
useEffect(() => {
if (!checkUserAdmin(currentUser)) {
history.push("/login");
}
}, [currentUser]);
return currentUser;
};
export default useAdmin;
Below is my index.js
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
</React.StrictMode>,
document.getElementById("root")
);
Reducers:
userTypes:
const userTypes = {
SET_CURRENT_USER: "SET_CURRENT_USER",
};
export default userTypes;
userActions:
import userTypes from "./user.types";
export const setCurrentUser = (user) => ({
type: userTypes.SET_CURRENT_USER,
payload: user,
});
userReducer:
import userTypes from "./user.types";
const INITIAL_STATE = {
currentUser: null,
};
const userReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case userTypes.SET_CURRENT_USER:
return {
...state,
currentUser: action.payload,
};
default:
return state;
}
};
export default userReducer;
rootReducer:
import { combineReducers } from "redux";
import userReducer from "./user/user.reducer";
export default combineReducers({
user: userReducer,
});
store.js
import { createStore, applyMiddleware } from "redux";
import logger from "redux-logger";
import rootReducer from "./rootReducer";
export const middlewares = [logger];
export const store = createStore(rootReducer, applyMiddleware(...middlewares));
export default store;
checkUserAdmin.js
export const checkUserAdmin = (currentUser) => {
if (!currentUser || !Array.isArray(currentUser.roles)) return false;
const { roles } = currentUser;
if (roles.includes("admin")) return true;
return false;
};
From the App.js, I console.log(currentUser) and this is what is shows:
I suggest adding an authPending state to your userReducer, initially true, and also set/cleared when the firestore logic is handing user changes.
userReducer & actions
const userTypes = {
SET_AUTH_PENDING: "SET_AUTH_PENDING",
SET_CURRENT_USER: "SET_CURRENT_USER",
};
const setAuthPending = pending => ({
type: userTypes.SET_AUTH_PENDING,
payload: pending,
});
const INITIAL_STATE = {
authPending: true,
currentUser: null,
};
const userReducer = (state = INITIAL_STATE, action) => {
switch (action.type) {
case userTypes.SET_CURRENT_USER:
return {
...state,
authPending: false
currentUser: action.payload,
};
case userTypes.SET_AUTH_PENDING:
return {
...state,
authPending: action.payload,
};
default:
return state;
}
};
app.js
const App = (props) => {
const {
setAuthPending, // <-- access action
setCurrentUser,
currentUser
} = props;
const admin = checkUserAdmin(currentUser);
console.log(admin);
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(async (userAuth) => {
setAuthPending(true); // <-- start auth pending
if (userAuth) {
const userRef = await handleUserProfile(userAuth);
userRef.onSnapshot((snapshot) => {
setCurrentUser({ // <-- will clear auth pending
id: snapshot.id,
...snapshot.data(),
});
});
} else {
setCurrentUser(null); // <-- clear user data and pending
}
});
return () => {
unsubscribe();
};
}, []);
return (
<div className="App">
<Switch>
...
</Switch>
</div>
);
};
const mapStateToProps = ({ user }) => ({
currentUser: user.currentUser,
});
const mapDispatchToProps = {
setAuthPending, // <-- wrap action creator in call to dispatch
setCurrentUser,
};
Hooks & Wrappers
For these I suggest abstracting the logic into custom Route components.
const AuthRoute = props => {
const { authPending, currentUser } = useSelector(state => state.user);
if (authPending) {
return "Loading..."; // or maybe a loading spinner
};
return currentUser ? (
<Route {...props} />
) : (
<Redirect to="/login" />
);
};
const AdminRoute = props => {
const { authPending, currentUser } = useSelector(state => state.user);
if (authPending) {
return "Loading..."; // or maybe a loading spinner
};
return checkUserAdmin(currentUser) ? (
<Route {...props} />
) : (
<Redirect to="/login" />
);
};
Then the routes become
<Switch>
<Route
exact
path="/"
render={() => (
<MainLayout>
<Homepage />
</MainLayout>
)}
/>
<Route
exact
path="/login"
render={() => (
<MainLayout>
<LoginPage />
</MainLayout>
)}
/>
<AuthRoute
exact
path="/profile"
render={() => (
<MainLayout>
<ProfilePage />
</MainLayout>
)}
/>
<AdminRoute
exact
path="/admin"
component={AdminHome}
/>
</Switch>
After this, you may want to look into persisting your redux state into localStorage, and repopulating your redux state from localStorage when you are instantiating the store (the preloadedState parameter) object when your app is loading. You can manage yourself or look into something like redux-persist.
When a user login you can store some values about the user in localStorage,like username or a token or just a login ,
localStorage.setItem(IS_LOGIN, true);
After that you can use that in your userReducer, when you initiate state you can directly determine the user is login or not.
const INITIAL_STATE = {
isLogin: localStorage.IS_LOGIN
};
now you can determine a user is login or not before the page load. If you wanna push user to the login page you can use in useEffect
useEffect(() => {
if (!isLogin) {
props.history.push("/login");
}
}, [isLogin]);
return isLogin;
};
when your app first loaded there is no user information on the userReducer, because of that when page load you will be directing to the login page.

How to use two contexts for authentication and subscription in Chrome extension?

I want to display the Stripe subscription form after user has signed up into the extension. I want to display home to a subscribed user. And whenever the user opens the extension the home should be displayed if he has already subscribed. If not, it should display subscription form.
But the problem is my app is displaying both the home and subscription form to a subscribed user.
Here is my private route code:
const PrivateRoute = ({ component: RouteComponent, ...rest }) => {
const { currentUser, subscriptionStatus } = useContext(AuthContext);
return (
<Route
{...rest}
render={(routeProps) =>
!!currentUser ? (
!!subscriptionStatus ? (
<RouteComponent {...routeProps} />
)
: (
<Redirect to={"/subscribe"} />
)
) : (
<Redirect to={"/login"} />
)
}
/>
);
};
This is my auth context provider:
export const AuthProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState(null);
const [pending, setPending] = useState(true);
const [emailVerified, setEmailVerified] = useState(true);
const [helper, setHelper] = useState(false);
const [subscriptionStatus, setSubscriptionStatus] = useState(null);
useEffect(() => {
app.auth().onAuthStateChanged(async(user) => {
setCurrentUser(user);
if(!user.emailVerified){
setEmailVerified(false);
}else{
setEmailVerified(true);
const fetchData = async () => {
const token = user && (await user.getIdToken());
const payloadHeader = {
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
};
const status = await fetch('http://localhost:3000/is-subscribed', payloadHeader).then(r => r.json());
if(status == 'active'){
setSubscriptionStatus(status);
setPending(false);
}else{
setPending(false);
}
}
fetchData();
}
});
}, []);
if (pending && helper) {
return <Loader />;
}
if(!emailVerified){
return <>Please verify your email</>
}
return (
<AuthContext.Provider
value={{
currentUser, subscriptionStatus
}}
>
{children}
</AuthContext.Provider>
);
};
Any idea on this?
The easiest option would be redirecting your customers to Checkout to pay, and handling the successful payments in your Firebase app via webhooks, but you can also use the custom flow if you prefer.

Why redux state is not render correctly?

Good day, I faced an issue when tried to push the user to the dashboard after the user login correctly but it didn't, here is the code below:
LoginForm.js
const { isLoading, isAuth, error, message } = useSelector(
(state) => state.login
);
const handleSubmit = (e) => {
e.preventDefault();
console.log(values);//values={email:'..', pass:'..'}
if (formValidation()) {
dispatch(NewUserLogin(values));
console.log(isAuth); //print false but in redux state print true
if (isAuth) history.push('/dashboard');
}
};
LoginAction.js
export const NewUserLogin = (formValues) => async (dispatch) => {
try {
dispatch(loginPending());
const { status, message } = await LoginAPIRequest(formValues);
if (status === 'success') {
dispatch(loginSuccess(message));
} else {
dispatch(loginFailure(message));
}
console.log(status);
console.log(message);
} catch (error) {
dispatch(loginFailure(error.message));
}
};
loginSlice.js
import { createSlice } from '#reduxjs/toolkit';
const initialState = {
isLoading: false,
isAuth: false,
error: '',
};
const loginSlice = createSlice({
name: 'Login',
initialState,
reducers: {
loginPending: (state) => {
state.isLoading = true;
},
loginSuccess: (state, { payload }) => {
state.isLoading = false;
state.isAuth = true;
state.message = payload;
state.error = '';
},
loginFailure: (state, { payload }) => {
//actions.payload or shortcut {payload}
state.isLoading = false;
state.error = payload;
},
},
});
const { reducer, actions } = loginSlice;
export const { loginPending, loginSuccess, loginFailure } = actions;
export default reducer;
userAPI.js
import { createEndpointsAPI, ENDPOINTS } from './index';
export const LoginAPIRequest = (formValues) => {
return new Promise(async (resolve, reject) => {
//call api
try {
await createEndpointsAPI(ENDPOINTS.LOGIN)
.create(formValues)
.then((res) => {
resolve(res.data);
if (res.data.status === 'success') {
resolve(res.data);
sessionStorage.setItem('accessJWT', res.data.accessJWT);
localStorage.setItem('sms', JSON.stringify(res.data.refreshJWT));
}
console.log(res.data);
})
.catch((err) => {
reject(err);
});
} catch (error) {
console.log(error);
reject(error);
}
});
};
index.js (root API)
import axios from 'axios';
export const ENDPOINTS = {
LOGIN: 'user/login',
LOGOUT: 'user/logout',
REGISTER: 'user/register',
};
const baseURL = 'http://localhost:3040/v2/';
export const createEndpointsAPI = (endpoint) => {
let url = baseURL + endpoint + '/';
return {
fetchAll: () => axios.get(url),
fetchById: (id) => axios.get(url + id),
create: (newData) => axios.post(url, newData),
update: (updateData, id) => axios.put(url + id, updateData),
delete: (id) => axios.delete(url + id),
};
};
App.js
<MuiThemeProvider theme={theme}>
<CssBaseline />
<Router>
<Switch>
<Route path='/' exact>
<Login />
</Route>
<PrivateRoute path='/dashboard'>
<Dashboard />
</PrivateRoute>
<Route path='*' component={() => '404 NOT FOUND'} />
</Switch>
</Router>
</MuiThemeProvider>
PrivateRoute.js
import { useSelector } from 'react-redux';
const PrivateRoute = ({ component: Component, ...rest }) => {
const { isAuth } = useSelector((state) => state.login);
console.log(isAuth);
return (
<Route
{...rest}
render={(props) => {
isAuth ? (
<Component {...props} />
) : (
<Redirect
to={{
pathname: '/',
state: { from: props.location },
}}
/>
);
}}
/>
);
};
export default PrivateRoute;
The problem is, isAuth is a redux state, it should return true when the user login correctly, but it's not, I console.log(isAuth) and it prints false for the first time even user login correctly, and if I click login one more time it prints true in the console log and redirects the user to the dashboard page. I don't know why isAuth is returned false for the first time when use is login correctly? Please help check the above code from top to bottom, I provide you everythings.
The log: console.log(isAuth); logs a stale closure, you could try an effect on isAuth and redirect when it's true.
Here is an example:
const Component = (propps) => {
const { isLoading, isAuth, error, message } = useSelector(
(state) => state.login
);
const handleSubmit = (e) => {
//...dispatches but doesn't check isAuth
};
useEffect(() => {
//go to dashboard if isAuth is true
if (isAuth) history.push('/dashboard');
}, [isAuth]);//run effect when isAuth changes
};

useContext causing unwanted re-render

I am having trouble with rerenders and memory leaks in my login form. The goal is to have a component that checks if the context's JWT is valid and if so redirects. However, when logging in and updating the context, the context causes a rerender when it should redirect anyways. What is the solution to this?
Edit: The issue seems to be that I am rerendering on authentication twice: once in Login and one in the SecuredRoute. Is there a more elegant solution?
useValidateToken.js
export default token => {
const [validateLoading, setLoading] = useState(true);
const [authenticated, setAuthenticated] = useState(false);
useEffect(() => {
fetch(`/validate_token`, {
method: "GET",
headers: { Authorization: "Bearer " + token }
})
.then(resp => {
if (resp.ok) setAuthenticated(true);
setLoading(false);
})
.catch(_ => setLoading(false));
}, [token]);
return { validateLoading, authenticated };
};
Login.js
function Login(props) {
const {token, setToken} = useContext(TokenContext)
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const { isLoading: validateLoading, authenticated } = useValidateToken(token);
const [shouldRedirect, setShouldRedirect] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [isInvalid, setIsInvalid] = useState(false);
function login() {
setIsLoading(true);
fetch(`/login`, { ... })
.then(resp => resp.json())
.then(body => {
if (body.jwt) {
setToken(body.jwt);
setShouldRedirect(true);
} else {
setIsInvalid(true);
setTimeout(function () { setIsInvalid(false) }, 3000)
setIsLoading(false);
}
})
.catch(_ => setIsLoading(false));
}
return validateLoading ? (
// Skipped code
) : shouldRedirect === true || authenticated === true ? (
<Redirect to={props.location.state ? props.location.state.from.pathname : "/"} />
) : (
<div className="login">
// Skipped code
<LoginButton loading={isLoading} login={login} error={isInvalid} />
</div>
</div>
);
}
The Route is secured using a custom component. This is done to secure routes and redirect to Login if there is an invalid token.
App.js
// Skipped code
const [token, setToken] = useState(null);
const { authenticated } = useValidateToken(token)
//Skipped code
<SecuredRoute exact path="/add-issue/" component={AddIssue} authenticated={authenticated} />
function SecuredRoute({ component: Component, authenticated, ...rest }) {
return (
<Route
{...rest}
render={props =>
authenticated === true ? (
<Component {...props} {...rest} />
) : (
<Redirect to={{ pathname: "/login", state: { from: props.location } }} />
)
}
/>
);
}

Categories

Resources