1

It's the first time I am working with role based routes. We have a fully working app for players. Now, we need to add Trainer page for that app. We store the users in Firebase all we need to add new key for users something like trainer: true|false manually. And in React Application we will redirect the users. For example:

    if (user.trainer) {
      <Redirect to={"/trainer"}
    } else {
      <Redirect to={"/"}
    }

And we need to manage other pages. For example: when trainers want to navigate to another pages where it's not allowed we need to redirect them back to "/trainer" or the opposite normal player can't navigate to "/trainer" page. Can someone please help me with that ?

I have centralized routes like this;

import SignInSignUp from "./pages/SignInSignUp/SignInSignUp";
import Home from "./pages/Home/Home";
import Help from "./pages/Help/Help";
import Profile from "./pages/Profile/Profile";
import Exercises from "./pages/Exercises/Exercises";
import Trainer from "./pages/Trainer/Trainer";


export const routes = [
  { isProtected: false, path: "/auth", component: SignInSignUp },
  { isProtected: true, path: "/", component: Home },
  { isProtected: true, path: "/help", component: Help },
  { isProtected: true, path: "/profile", component: Profile },
  { isProtected: true, path: "/exercises", component: Exercises },
  { isProtected: true, path: "/trainer", component: Trainer },
];

And I loop the routes like that;

import "./App.scss";
import { Switch, Route, Redirect } from "react-router-dom";
import Navbar from "./components/Navbar/Navbar";
import ProtectedRouter from "./utils/ProtectedRouter";

import { routes, navbarPaths } from "./routes";

function App() {


  return (
    <div className="App">

      <Switch>
        {routes.map(
          ({
            isProtected,
            component,
            path,
          }: {
            isProtected: boolean;
            component: any;
            path: string;
          }) => {
            const RouteWrapper = isProtected ? ProtectedRouter : Route;
            return (
              <RouteWrapper
                exact
                key={path}
                path={path}
                component={component}
              />
            );
          }
        )}
        <Route exact path="/player*" render={() => (<Redirect to={"/"} />)} />          
      </Switch>

      <Route path={navbarPaths} exact component={Navbar} />
    </div>
  );
}

export default App;

And this is my Protected Route Component

import { Route, Redirect } from "react-router-dom";
import { useAuthStatus } from "../firebase/useAuthStatus.hook";

const ProtectedRouter = ({ component: Component, ...rest }: any) => {
  const { loggedIn, checkingStatus } = useAuthStatus();
  return (
    <Route
      {...rest}
      render={(props) => {
        if (!checkingStatus) {
          if (loggedIn) {
            return <Component />;
          } else {
            return (
              <Redirect
                to={{
                  pathname: "/auth",
                  state: {
                    from: props.location,
                  },
                }}
              />
            );
          }
        }
      }}
    />
  );
};

export default ProtectedRouter;
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
Ruslan Bairamovi
  • 378
  • 3
  • 22
  • Are you just asking how to conditionally redirect in your `ProtectedRouter` component? Where you access the `user` object to check role(s)? – Drew Reese Jan 13 '22 at 16:56

2 Answers2

1

If I'm understanding your question correctly, you want to conditionally change the redirect target in your ProtectedRouter component based on a user's role.

Not tested at all, but I believe the following will resolve your question.

Refactor your ProtectedRoute to take the isProtected, trainer, and passed route props. If the route is unprotected, check the user's trainer role for a match and conditionally render a route or redirect to "/trainer" or "/". If the route is protected and the user is authenticated, again check the user's trainer role for a match and conditionally render a route or redirect to "/trainer" or "/".

const ProtectedRouter = ({ isProtected, trainer, ...props }: any) => {
  const location = useLocation();
  const { loggedIn, checkingStatus } = useAuthStatus();
  const user = /* business logic to get user object */

  if (checkingStatus) return null;

  if (!isProtected || loggedIn) {
    return user.trainer === trainer
      ? <Route {...props} />
      : <Redirect to={user.trainer ? "/trainer" : "/"} />;
  }

  return (
    <Redirect
      to={{
        pathname: "/auth",
        state: { from: location },
      }}
    />
  );
};

Update the routes to add a trainer property.

export const routes = [
  { isProtected: false, path: "/auth", component: SignInSignUp },
  { isProtected: true, path: "/", component: Home },
  { isProtected: true, path: "/help", component: Help },
  { isProtected: true, path: "/profile", component: Profile },
  { isProtected: true, path: "/exercises", component: Exercises },
  { isProtected: true, path: "/trainer", component: Trainer, trainer: true },
];

Update App to map all the routes to the custom route component.

function App() {
  ...

  return (
    <div className="App">
      <Switch>
        {routes.map(props => <ProtectedRouter key={props.path} exact {...props} />)}
        <Route exact path="/player*" render={() => (<Redirect to={"/"} />)} />          
      </Switch>

      <Route path={navbarPaths} exact component={Navbar} />
    </div>
  );
}
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
0

import { v4 as uuidv4 } from "uuid";

const routes = [
    {
        id: uuidv4(),
        isProtected: false,
        exact: true,
        path: "/home",
        component: param => <Overview {...param} />,
    },
    {
        id: uuidv4(),
        isProtected: true,
        exact: true,
        path: "/protected",
        component: param => <Overview {...param} />,
        allowed: [...advanceProducts], // subscription
    },
    {
        // if you conditional based rendering for same path
        id: uuidv4(),
        isProtected: true,
        exact: true,
        path: "/",
        component: null,
        conditionalComponent: true,
        allowed: {
            [subscription1]: param => <Overview {...param} />,
            [subscription2]: param => <Customers {...param} />,
        },
    },
]

// Navigation Component
import React, { useEffect, useState } from "react";
import { useSelector } from "react-redux";
import { Switch, Route, useLocation } from "react-router-dom";

// ...component logic
<Switch>
    {routes.map(params => {
        return (
            <ProtectedRoutes
                exact
                routeParams={params}
                key={params.path}
                path={params.path}
            />
        );
    })}
    <Route
        render={() => {
            props.setHideNav(true);
            setHideHeader(true);
            return <ErrorPage type={404} />;
        }}
    />
</Switch>

// ProtectedRoute component
import React from "react";
import { Route } from "react-router-dom";
import { useSelector } from "react-redux";

const ProtectedRoutes = props => {
    const { routeParams } = props;
    const currentSubscription = 'xyz'; // your current subscription;
    if (routeParams.conditionalComponent) {
        return (
            <Route
                key={routeParams.path}
                path={routeParams.path}
                render={routeParams.allowed[currentSubscription]}
            />
        );
    }
    if (routeParams.isProtected && routeParams.allowed.includes(currentSubscription)) {
        return (
            <Route key={routeParams.path} path={routeParams.path} render={routeParams?.component} />
        );
    }
    if (!routeParams.isProtected) {
        return (
            <Route key={routeParams.path} path={routeParams.path} render={routeParams?.component} />
        );
    }
    return (
    <Route
        render={() => {
            return <ErrorPage type={404} />;
        }}
    />
);
};

export default ProtectedRoutes;

Would like to add highlight never forget to give path as prop to ProtectedRoute, else it will not work.

Nikita Kumari
  • 309
  • 3
  • 7