0

I'm trying to use the onEnter of Router from react-router-dom to validate if a user is authenticate. I'm keeping the authentication in the login reducer.

Initial State:

const initialState = {
    logged: false,
    userName: null
}

In my App.js I have my Router

import React from 'react';
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link
} from "react-router-dom";
import { useSelector } from "react-redux";
import Home from './Home';
import Login from './Login';

function App() {
  const login = useSelector((state) => state.login);

  const requireAuth = (nextState, replace, next) => {
      console.log(login)
    if(!login.logged) {
      replace({
        pathname: "/login",
        state: {nextPathname: nextState.location.pathname}
      });
    }
    next();
  }

  return (
    <Router>
      <Switch>
        <Route exact path="/" component={Home} onEnter={requireAuth} />
        <Route exact path="/login" component={Login} />
      </Switch>
    </Router>
  )
}

export default App;

It is not redirecting to the /login. Actually, it is not calling the requireAuth function because the browser does not show the log that I have inside the function. What am I missing in the Router settings? By the way, is it an appropriate way to check if a user is authenticate before navigating to a page?

Thanks

Updated code using PrivateRoute. Error: Error: Maximum update depth exceeded.

import React from 'react';
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Redirect
} from "react-router-dom";
import { useSelector } from "react-redux";
import Home from './Home';
import Login from './Login';

function App() {
  
  const PrivateRoute = ({ children, ...rest }) => {
    const login = useSelector((state) => state.login);
      console.log(login);
    return (
      <Route
        {...rest}
        render={({ location }) =>
          login.logged ? (
            children
          ) : (
            <Redirect
              to={{
                pathname: "/login",
                state: { from: location }
              }}
            />
          )
        }
      />
    );
  }

  return (
    <Router>
      <Switch>
        <PrivateRoute path="/">
          <Home />
        </PrivateRoute>
        <Route path="/login">
          <Login />
        </Route>
      </Switch>
    </Router>
  )
}

export default App;

1 Answer 1

3

no, the best approach is what you can see in react-router example page.

you better define a route to your private pages like this:

function PrivateRoute({ children, ...rest }) {
  const login = useSelector((state) => state.login);
  return (
    <Route
      {...rest}
      render={({ location }) =>
        login ? (
          children
        ) : (
          <Redirect
            to={{
              pathname: "/login",
              state: { from: location }
            }}
          />
        )
      }
    />
  );
}

and then use it like this:

<Switch>
    <Route path="/public">
       <PublicPage />
    </Route>
    <Route path="/login">
       <LoginPage />
    </Route>
    <PrivateRoute path="/protected">
       <ProtectedPage />
    </PrivateRoute>
</Switch>

update:

you updated your post so, for those who may make this mistake:

in the updated code we have this switch structure:

<Switch>
    <PrivateRoute path="/">
      <Home />
    </PrivateRoute>
    <Route path="/login">
      <Login />
    </Route>
</Switch>

when we redirect user to /login page. it matches the first route (/) too. so Switch select the first PrivateRoute to render and it makes an infinite loop.

there is two ways to tackle the problem:

first: put longer and more specific routes first within Switch hierarchy. for example in your code you could do this:

<Switch>
    <Route path="/login">
      <Login />
    </Route>
    <PrivateRoute path="/">
      <Home />
    </PrivateRoute>
</Switch>

second: use exact in more generic routes that comes first:

<Switch>
    <PrivateRoute exact path="/">
      <Home />
    </PrivateRoute>
    <Route path="/login">
      <Login />
    </Route>
</Switch>
Sign up to request clarification or add additional context in comments.

5 Comments

From you example, when I try to access localhost:3000/login it returns Error: Maximum update depth exceeded because it calls the PrivateRoute method multiple times
try <PrivateRoute exact path="/">. (add exact keyword)
remember when your url is /login it matches both path you defined. (/ and /login) and it chooses the first one, because of the Switch component
Thanks! Every path that I add, will match the first /? In this case, should I move the / to the end of the Switch?
yes, or as I updated my answer, you can use exact property on your root / route.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.