Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Refresh on protected Routes React Router with firebase auth

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;
like image 229
Vinicius Barbosa de Medeiros Avatar asked Mar 04 '18 02:03

Vinicius Barbosa de Medeiros


2 Answers

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!)

like image 102
udarabibile Avatar answered Nov 03 '22 10:11

udarabibile


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="/" />;
    }}
  />
);
like image 20
mchouhan_google Avatar answered Nov 03 '22 10:11

mchouhan_google