React.js 简单的基于角色的路由

React.js Simple Role Based Routes

这是我第一次使用基于角色的路由。我们为玩家提供了一个功能完备的应用程序。现在,我们需要为该应用程序添加 Trainer 页面。我们将用户存储在 Firebase 中,我们需要手动为用户添加新密钥,例如 trainer: true|false。在 React Application 中,我们将重定向用户。 例如:

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

并且我们需要管理其他页面。例如:当培训师想要导航到另一个不允许的页面时,我们需要将他们重定向回 "/trainer" 或对面的普通玩家无法导航到 "/trainer" 页面。有人可以帮我吗?

我有这样的集中路由;

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 },
];

然后我就这样循环路由;

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;

这是我的受保护路由组件

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;

如果我没有正确理解你的问题,你想根据用户的角色有条件地更改 ProtectedRouter 组件中的重定向目标。

完全没有测试,但我相信以下内容会解决您的问题。

重构您的 ProtectedRoute 以采用 isProtectedtrainer 和传递的路线道具。如果路由未受保护,请检查用户的 trainer 角色是否匹配,并有条件地呈现路由或重定向到 "/trainer""/"。如果路由受保护 并且 用户已通过身份验证,请再次检查用户的 trainer 角色是否匹配并有条件地呈现路由或重定向到 "/trainer""/".

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 },
      }}
    />
  );
};

更新 routes 以添加 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, trainer: true },
];

更新 App 以将所有路由映射到自定义路由组件。

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>
  );
}

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;

想要添加亮点永远不要忘记将路径作为 ProtectedRoute 的属性,否则将无法工作。