Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add protected routes in react redux

I have created a login component on which I have all the logic stuff.

The login reducer is:

const openState = {
  loggedIn: null,
  user: null
}

export default (state = openState, action) => {
  switch (action.type) {
    case LOGIN:
      return { ...state, loggedIn: true, user: action.payload }
    case LOGOUT:
      return { ...state, loggedIn: false, user: null }
    default:
      return openState
  }
}

The Action :

export const logIn = (user) => {
  return {
    type: LOGIN,
    payload: user
  }
}

export const logOut = () => {
  return {
    type: LOGOUT
  }
}

everything is working just fine but I'm not sure how to pass the loggedIn and user props from action into the routes component in order to secure all routes:

const MainRoutes = props => {
  const { loggedIn } = props;

  console.log(props.loggedIn)

return (
  <Router history={history}>
    <Baseline />
    <Menu/>
    <Container maxWidth="md">
      <Switch>
        <Route exact path="/Login" component={Login} />
        <Route exact path="/Carousel" component={Carousel} />
        <Route exact path="/Stepper" component={Stepper} />
        <Route component={NotFound} />
      </Switch>
    </Container>
  </Router>
);
}

const mapStateToProps = (state) => {
return { loggedIn: state.loggedIn };
};

export default connect(mapStateToProps)(MainRoutes);

If I'll console.log the loggedIn props I get undefined :| Based on loggedIn I can create a logic into the routes component.

like image 438
RulerNature Avatar asked Feb 15 '21 10:02

RulerNature


People also ask

What are protected routes in react?

This means that users must first meet certain conditions before accessing that specific route. For instance, your application can require only logged-in users be able to visit the dashboard page. In this tutorial, you are going to learn how you create protected routes in a React application.

How to create a protected route?

Protected Route 1 create a functional component that accepts component and other route details as props, and then 2 check a condition to confirm if user is authenticated or not. (In our case, we'll be getting isAutheticated from localStorage) 3 if the value is true, render the component, else, Redirect route to /signin page.

How do I prevent unauthorized users from accessing my react page?

Preventing unauthorized users from accessing your React page is critical for security. Here's how to do it. Protected routes are those routes that only grant access to authorized users. This means that users must first meet certain conditions before accessing that specific route.

Is there a navigate component in react router V6?

There is no direct thing in React-Router for this. You need to write a wrapper like this. However, I'm not sure what Navigate is in your code. "If you prefer to use a declarative API for navigation (ala v5's Redirect component), v6 provides a Navigate component." You can find more info here github.com/ReactTraining/react-router/blob/dev/docs/…


2 Answers

You could simply define a custom ProtectedRoute component that'll be connected to redux state. In your case it should map state.auth.loggedIn and state.auth.user to props and perform a Redirect if those values are falsy:

import React from "react";
import { Route, Redirect } from "react-router-dom";
import PropTypes from "prop-types";


const ProtectedRoute = (props) => {
  const { redirectPath, component, user, loggedIn, ...routeProps} = props;
  const Component = component;
  const isAccessible = Boolean(user) && loggedIn;

  return (
    <Route
      {...routeProps}
      render={props => {
        if (isAccessible) return <Component {...props} />;
        return <Redirect to={{ pathname: redirectPath || "/Login" }} />;
      }}
    />
  );
};

ProtectedRoute.propTypes = {
  path: PropTypes.string.isRequired,
  redirectPath: PropTypes.string,
  component: PropTypes.oneOfType([
    PropTypes.shape({ render: PropTypes.func.isRequired }),
    PropTypes.func
  ]),
};

const mapStateToProps = (state) => {
  return { 
    loggedIn: state.auth.loggedIn, 
    user: state.auth.user 
  };
};

export default connect(mapStateToProps)(ProtectedRoute);

With this in place your MainRoutes won't need connect anymore:

const MainRoutes = props => {
  return (
    <Router history={history}>
      ...
      <Container maxWidth="md">
        <Switch>
          <Route exact path="/Login" component={Login} />
          <ProtectedRoute exact path="/Carousel" component={Carousel} />
          <ProtectedRoute exact path="/Stepper" component={Stepper} />
          <Route component={NotFound} />
        </Switch>
      </Container>
    </Router>
  );
}

export default MainRoutes;

Update:

If you want to keep auth state after page refresh you'll need to perform some extra setup of your redux store. The main idea is to subscribe on every action and put a fresh copy of state into localStorage or cookies and also to persist initialState from selected storage before your app boots up. Here is an example:

function getFromStorage(key) {
  try {
    return JSON.parse(window.localStorage.getItem(key)) || {};
  } catch (err) {
    return {};
  }
}

const initialState = getFromStorage("APP_STATE");

const store = createStore(
  rootReducer,
  initialState, // should go after root reducer
  ...
);

store.subscribe(() => {
  window.localStorage.setItem("APP_STATE", JSON.stringify(store.getState()));
});

And here's the working sandbox (bootstrapped by SuleymanSah)

like image 99
streletss Avatar answered Sep 28 '22 09:09

streletss


As others already described, you had better to create a Protected Route to accomplish what you want. You simply redirect the user to the Login route if he/she is not logged in.

Here is my implementation: (codesandbox)

import React from "react";
import { Route, Redirect } from "react-router-dom";
import { connect } from "react-redux";

const ProtectedRoute = ({
  path,
  component: Component,
  render,
  loggedIn,
  ...rest
}) => {
  return (
    <Route
      path={path}
      {...rest}
      render={(props) => {
        if (loggedIn) {
          return Component ? <Component {...props} /> : render(props);
        }
        return (
          <Redirect
            to={{
              pathname: "/login",
              state: { from: props.location }
            }}
          />
        );
      }}
    />
  );
};

const mapStateToProps = (state) => {
  const { loggedIn } = state.auth;
  return {
    loggedIn
  };
};

export default connect(mapStateToProps)(ProtectedRoute);
like image 29
SuleymanSah Avatar answered Sep 28 '22 08:09

SuleymanSah