I'm building a todo web app using firebase, react and redux. So I have protected rotes such as "/todos/add"
, "/dashboard"
and guests Routes as "/"
,"/signup"
, "/login"
which can't be accessed if you're are authenticated.
My problem is when I'm authenticated in the "/todos/add"
route and I reload the page the app reloads and my auth actions are dispatched putting my user in the redux store, but I'm redirect to "/login"
an then to "/dashboard"
. but I want to be on the same page before the reload. my code:
PrivateRoute.js
import React from "react";
import { connect } from "react-redux";
import { Route, Redirect } from "react-router-dom";
import PropTypes from "prop-types";
const PrivateRoute = ({
isAuthenticated,
isLoading,
component: Component,
...rest
}) => (
<Route
{...rest}
render={props =>
isAuthenticated ? (
<Component {...props} {...rest} />
) : (
<Redirect to="/login" />
)
}
/>
);
const mapStateToProps = state => ({
isAuthenticated: !!state.user.uid,
isLoading: state.user.isLoading
});
PrivateRoute.propTypes = {
component: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool.isRequired,
isLoading: PropTypes.bool.isRequired
};
export default connect(mapStateToProps)(PrivateRoute);
GuestRoute.js
import React from "react";
import { connect } from "react-redux";
import { Route, Redirect } from "react-router-dom";
import PropTypes from "prop-types";
const GuestRoute = ({ isAuthenticated, component: Component, ...rest }) => (
<Route
{...rest}
render={props =>
!isAuthenticated ? (
<Component {...props} />
) : (
<Redirect
to="/dashboard"
/>
)
}
/>
);
const mapStateToProps = state => ({
isAuthenticated: !!state.user.uid,
isLoading: !!state.user.isLoading
});
GuestRoute.propTypes = {
component: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool.isRequired,
isLoading: PropTypes.bool.isRequired
};
export default connect(mapStateToProps)(GuestRoute);
App.js
import React from "react";
import { Route, Switch } from "react-router-dom";
// COMPONENTS
import HomePage from "./components/pages/homepage/HomePage";
import SignupPage from "./components/pages/signuppage/SignupPage";
import LoginPage from "./components/pages/loginpage/LoginPage";
import DashboardPage from "./components/pages/dashboardpage/DashboardPage";
// HOC
import Layout from "./components/hoc/layout/Layout";
// ROUTES
import PrivateRoute from "./routes/PrivateRoute";
import GuestRoute from "./routes/GuestRoute";
import AddTodosPage from "./components/pages/todos/add/AddTodosPage";
const App = () => (
<Layout>
<Switch>
<Route path="/" exact component={HomePage} />
<GuestRoute path="/signup" exact component={SignupPage} />
<GuestRoute path="/login" exact component={LoginPage} />
<PrivateRoute path="/todos/add" exact component={AddTodosPage} />
<PrivateRoute path="/dashboard" exact component={DashboardPage} />
</Switch>
</Layout>
);
export default App;
FIX
Set isLoading
to true
by default, which would be the value at refresh. (Set isLoading
to false
after firebase authentication.) And condition in <PrivateRoute>
should be (isLoading || isAuthenticated )
.
But Important : Here isLoading
and isAuthenticated
should be set together after firebase authentication response. Or isAuthenticated
should be set accordingly before isLoading
is set to false
.
Explanation
As soon as you refresh in <PrivateRoute>
of /todos/add
, it'll look for isAuthenticated
value. Here seems it's false
, and therefore will be redirected to /login
route.
When in <GuestRoute>
of /login
, it'll again look at !isAuthenticated
value. By this time isAuthenticated
value seems to be true
making it redirect back to /dashboard
.
So from this observation it can be seen that isAuthenticated
value is not set to true
as soon as refreshed. But rather this firebase authentication request is asynchronous action, where there will be some notable in response. But before the response, redirecting happens taking default isAuthenticated
value or undefined
as value. In-between this re-directions, firebase authentication call will be done and set isAuthenticated
to true
for the final redirection to /dashboard
.
But here make use of isLoading
value to delay redirections. Set isLoading
to true
by default, which would be the value at refresh. (Set isLoading
to false
after firebase authentication.) And condition in <PrivateRoute>
should be (isLoading || isAuthenticated )
. IMPORTANT : Here isLoading
and isAuthenticated
should be set together after firebase authentication response. Or isAuthenticated
should be set accordingly before isLoading
is set to false
. (Else if isLoading
set to false
and if isAuthenticated
still remains false
or undefined
it'll be redirected!)
Instead of rendering your route, try to render something like 'a loading state'. Otherwise you will end up displaying your private route to an unauthenticated user if the auth call takes some time to resolve.
const PrivateRoute = ({
component: Component,
permission: Permission,
waitingForAuth: Waiting,
...rest
}) => (
<Route
{...rest}
render={props => {
if (Waiting) return <LinearProgress />;
return Permission ? <Component {...props} /> : <Redirect to="/" />;
}}
/>
);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With