Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to dynamically use react routing

I have a application where I have two logins one for superAdmin and other for 'Admin'.

  1. I have several pages One which is common (home page) both users have excess to that.

  2. then I have several other pages which are some for admin and others are for superAdmin.

  3. Now when I open my page I am trying to go '/' this route (My home route). What I am trying to do

  4. Now If I am logged in as admin and admin user is typing some superAdmin url in address bar I want that to be redirect to current admin route

  5. Same goes for superAdmin as well

  6. both the user I want to restrict to excess each-others route

  7. And if I am admin user or superAdmin user and trying to logged in and trying to excess authenticated route I should be redirect to home page

What I have done

I have created one component (Dynamic route) here I am checking what user is trying to do. and in route.js in my routing file I am passing props as guest,superAdmin and admin

Dynamicroute.js code

I have created my context to store the user once they logged in

    export default function Dynamicroute(props) {
    const { user } = useAuthState();  // this I am getting from my context
    console.log(user);

    if (props.partner && !user) {
        console.log('admin not logedin');
        return <Redirect to="/admin" />;
    } else if (props.admin && !user) {
        console.log('superAdmin not loged in');
        return <Redirect to="/superAdmin" />;
    } else if (props.admin && user.role === 'admin') {
        console.log('admin logedin');
        return <Redirect to="/admin_home" />;
    } else if (props.admin && user.role === 'superAdmin') {
        console.log('super admin loged in');
        return <Redirect to="/superadmin_home" />;
    } else if (props.guest && user) {
        console.log('guest');
        return <Redirect to="/" />;
    } else {
        return <Route component={props.component} {...props} />;
    }
}

My route.js

<DuynamicRoute exact path="/" component={Home} guest />
<DuynamicRoute path="/admin" component={loginAdmin} guest />
<DuynamicRoute path="/superAdmin" component={loginSuperAdmin} guest />
<DuynamicRoute path="/admin_home" component={admin_home} admin/>
<DuynamicRoute path="/superAdmin_home" component={superAdmin_home} superAdmin/>

Issue I am facing

I don't know what issue I am facing it is redirecting me to that route on login but content is not loading If I console something on that page I am not able to get that, the page is getting blank.

I am following this lecture from 25:00 timing

I am getting this blank page

Edited

Here is my code sand box

Please do check this

Edit

admin and super admin are going to be loged in different browsers, So just do not want admin to access super admin and vice-versa if they type in url each other's rout

like image 817
manish thakur Avatar asked Feb 04 '21 16:02

manish thakur


1 Answers

For better management and development of the program along with the best practices, Do the Authorization in React.js as follows:

Demo on Codesandbox

First: You need a class for checking permissions and routes/pages configs like below:

class AppUtils {
  static setRoutes(config) {
    let routes = [...config.routes];

    if (config.auth) {
      routes = routes.map((route) => {
        let auth = config.auth ? [...config.auth] : null;
        auth = route.auth ? [...auth, ...route.auth] : auth;
        return {
          ...route,
          auth
        };
      });
    }

    return [...routes];
  }

  static generateRoutesFromConfigs(configs) {
    let allRoutes = [];
    configs.forEach((config) => {
      allRoutes = [...allRoutes, ...this.setRoutes(config)];
    });
    return allRoutes;
  }

  static hasPermission(authArr, userRole) {
    /**
     * If auth array is not defined
     * Pass and allow
     */
    if (authArr === null || authArr === undefined) {
      // console.info("auth is null || undefined:", authArr);
      return true;
    } else if (authArr.length === 0) {
      /**
       * if auth array is empty means,
       * allow only user role is guest (null or empty[])
       */
      // console.info("auth is empty[]:", authArr);
      return !userRole || userRole.length === 0;
    } else {
      /**
       * Check if user has grants
       */
      // console.info("auth arr:", authArr);
      /*
            Check if user role is array,
            */
      if (userRole && Array.isArray(userRole)) {
        return authArr.some((r) => userRole.indexOf(r) >= 0);
      }

      /*
            Check if user role is string,
            */
      return authArr.includes(userRole);
    }
  }
}

export default AppUtils;

Second: You need Authorization component for handling routes like below:

import React, { Component } from "react";
import AppUtils from "utils/AppUtils";
import { matchRoutes } from "react-router-config";
import { withRouter } from "react-router-dom";
import AppContext from "context/AppContext";

class AppAuthorization extends Component {
  constructor(props, context) {
    super(props);
    const { routes } = context;
    this.state = {
      accessGranted: true,
      routes
    };
  }

  componentDidMount() {
    if (!this.state.accessGranted) {
      this.redirectRoute();
    }
  }

  componentDidUpdate() {
    if (!this.state.accessGranted) {
      this.redirectRoute();
    }
  }

  static getDerivedStateFromProps(props, state) {
    const { location, userRole } = props;
    const { pathname } = location;

    const matched = matchRoutes(state.routes, pathname)[0];

    return {
      accessGranted: matched
        ? AppUtils.hasPermission(matched.route.auth, userRole)
        : true
    };
  }

  shouldComponentUpdate(nextProps, nextState) {
    return nextState.accessGranted !== this.state.accessGranted;
  }

  redirectRoute() {
    const { location, userRole, history } = this.props;
    const { pathname, state } = location;
    const redirectUrl = state && state.redirectUrl ? state.redirectUrl : "/";

    /*
        User is guest
        Redirect to Login Page
        */
    if (!userRole || userRole.length === 0) {
      history.push({
        pathname: "/login",
        state: { redirectUrl: pathname }
      });
    } else {
      /*
        User is member
        User must be on unAuthorized page or just logged in
        Redirect to dashboard or redirectUrl
        */
      history.push({
        pathname: redirectUrl
      });
    }
  }

  render() {
    // console.info('App Authorization rendered', accessGranted);
    return this.state.accessGranted ? (
      <React.Fragment>{this.props.children}</React.Fragment>
    ) : null;
  }
}

// AppAuthorization.defaultProps = {
//   userRole: [] // You can manage roles by redux or any state managements
// };

AppAuthorization.contextType = AppContext;

export default withRouter(AppAuthorization);

Third: You need authRoles file or remote for managing roles on client like below:

/**
 * Authorization Roles
 */
const authRoles = {
  admin: ["admin"],
  superAdmin: ["superAdmin"],
  user: ["user"],
  onlyGuest: []
};

export default authRoles;

Forth: If you want to move forward with this logic, you have to implement the structure of your pages as follows:

src
 |---pages
      |---home
           |---HomePage.jsx
           |---HomePageConfig.jsx
      |
      |- The rest of the pages should be implemented in the same way

For example: When you want to implement a page that only the admin can see (admin home page config):

import React from "react";
import authRoles from "auth/authRoles";

export const AdminHomePageConfig = {
  auth: authRoles.admin,
  routes: [
    {
      path: "/admin",
      exact: true,
      component: React.lazy(() => import("./HomePage"))
    }
  ]
};

Or the home page that everyone can see:

import React from "react";

export const HomePageConfig = {
  routes: [
    {
      path: "/",
      exact: true,
      component: React.lazy(() => import("./HomePage"))
    }
  ]
};

According to the example above, you can enter the auth prop with the role here, to restrict access to the page.

To get a closer look at this logic, I implemented it in the Codesandbox, which you can see via the link below:

Demo on Codesandbox

Notice: The above demo needs to be more complete, and instead of storing user roles in the state, it is better to use state management packages (redux, ...) and also perform login operations through cookies.

like image 73
Mohammad Oftadeh Avatar answered Sep 29 '22 07:09

Mohammad Oftadeh