使用React Router V6的受保护路由

时间:2020-06-15 08:36:15

标签: reactjs react-router react-router-dom

用新版本的react-router 6编写ProtectedRoute的正确方法是什么? 我写了这个,但这不是一条路线

const PrivateRoute = ({ component: Component, ...props }) => {   
  if (!Component) return null;

  return props.isAuthenticated
    ? <Component />
    : <Navigate to={props.redirectLink} /> }

export default PrivateRoute;

6 个答案:

答案 0 :(得分:4)

这是我使用 useRoutes 实现私有路由的工作示例。

App.js

import routes from './routes';
import { useRoutes } from 'react-router-dom';

function App() {
  const { isLoggedIn } = useSelector((state) => state.auth);

  const routing = useRoutes(routes(isLoggedIn));

  return (
    <>
      {routing}
    </>
  );
}

routes.js

import { Navigate,Outlet } from 'react-router-dom';

    const routes = (isLoggedIn) => [
      {
        path: '/app',
        element: isLoggedIn ? <DashboardLayout /> : <Navigate to="/login" />,
        children: [
          { path: '/dashboard', element: <Dashboard /> },
          { path: '/account', element: <Account /> },
          { path: '/', element: <Navigate to="/app/dashboard" /> },
          {
            path: 'member',
            element: <Outlet />,
            children: [
              { path: '/', element: <MemberGrid /> },
              { path: '/add', element: <AddMember /> },
            ],
          },
        ],
      },
      {
        path: '/',
        element: !isLoggedIn ? <MainLayout /> : <Navigate to="/app/dashboard" />,
        children: [
          { path: 'login', element: <Login /> },
          { path: '/', element: <Navigate to="/login" /> },
        ],
      },
    ];
    
    export default routes;

答案 1 :(得分:3)

您需要编写一个小的包装,并使用Navigate组件进行重定向。另外,您需要渲染一条路线

const Container = ({Component, redirectLink, isAuthenticated, ...props}) => {
  if(!isAuthenticated) {
       return <Navigate to={redirectLink} />;
   }

   return <Component {...props} />
}
const PrivateRoute = ({ component: Component, redirectLink, isAuthenticated, path, ...props }) => {   

  return <Route path={path} element={<Container redirectLink={redirectLink} isAuthenticate={isAuthenticated} Component={Component}/>
}

export default PrivateRoute;

您可以在the github docs

上找到迁移指南。

答案 2 :(得分:2)

所有不错的选择。您还可以根据身份验证状态(或任何其他状态)简单地呈现不同的路由处理。您不必使用原始 Javascript 对象方法。

不要忘记您可以使用立即调用的匿名内部函数 (() => COMPONENT)() 来动态决定哪个组件处理特定的 <Route/>

这些示例可能尚未包含在 v6 的初步文档中,因为处理私有 <Route/> 实际上非常简单。

例如

<Routes>
      {state.authed ?
        // Wait until we have the current user...
        currentUser ?
          <Route
            path='/'
            element={(() => {
              // Show a "no access" message if the user is NOT an App Admin doesn't have access to any schools at all (which includes not having access to anything INSIDE any school either)
              if (!currentUser.appAdministrator && currentUser.schoolIds?.length === 0) return <AdminNoAccess />
              return <Outlet />
            })()}
          >
            <Route
              path='/'
              element={(() => {
                // If the user is a super user, we return the <SuperAdmin /> component, which renders some of its own routes/nav.
                if (currentUser.appAdministrator) return <SuperAdmin />
                return <Outlet />
              })()}
            >
              <Route
                path='schools'
                element={(() => {
                  if (currentUser.schoolIds?.length === 1) {
                    return <Navigate to={`schools/schoolId`} />
                  } else {
                    return <AdminSchools />
                  }
                })()}
              />

              <Route path='users' children={<Users />} />
            </Route>

            <Route path={`schools/:schoolId`} element={<AdminSchool />} />

            <Route path='*' element={<Navigate to='schools' />} />
          </Route>
          :
          null
        :
        <>
          <Route path='login' element={<Login />} />
          <Route path='signup' element={<Signup />} />
          <Route path='forgot-password' element={<ForgotPassword />} />
          <Route path='reset-password' element={<ResetPassword />} />

          <Route path='*' element={<Navigate to='login' />} />
        </>
      }
    </Routes>

答案 3 :(得分:1)

这是一个可行的示例。

import React from 'react';
import { Route, Navigate } from 'react-router-dom';

const PrivateRoute = ({ component: Component, redirectTo, isAuth, path, ...props }) => {
    if(!isAuth) {
        return <Navigate to={redirectTo} />;
    }
    return <Route path={path} element={<Component />} />
};

export default PrivateRoute;

用法:

<Routes>
     <Route path="app" element={<DashboardLayout />}>
         <PrivateRoute isAuth={true} path="account" component={AccountView}  redirectTo='/login'/>
     </Route>
 </Routes>

答案 4 :(得分:1)

这是我最新的使用react-router v6 beta的实现。我不知道如何使用useRoutes实现受保护的路由。他们的文档应该添加一个示例,说明如何以两种方式实现受保护/专用路由。

ProtectedRoute组件

import React from 'react';
import PropTypes from 'prop-types';
import { Route } from 'react-router-dom';
import Forbidden from '../../views/errors/Forbidden';
import { useAuth } from '../../contexts/AuthContext';

const ProtectedRoute = ({ roles, element, children, ...rest }) => {
  const { user, login } = useAuth();

  if (!user) {
    login();
    return <></>;
  }

  if (roles.length > 0) {
    const routeRoles = roles.map((role) => role.toLowerCase());
    const userRoles = (user && user.roles ? user.roles : []).map((role) => role.toLowerCase());
    if (miscUtils.intersection(routeRoles, userRoles).length === 0) {
      return <Forbidden />;
    }
  }

  return (
    <Route element={element} {...rest}>
      {children}
    </Route>
  );
};

ProtectedRoute.propTypes = {
  roles: PropTypes.arrayOf(PropTypes.string),
  element: PropTypes.element,
  children: PropTypes.node,
};

ProtectedRoute.defaultProps = {
  roles: [],
  element: null,
  children: null,
};

export default ProtectedRoute;

AppRoutes组件

import React from 'react';
import { Routes, Route, Navigate, Outlet } from 'react-router-dom';
import Login from './components/oauth/Login';
import Logout from './components/oauth/Logout';
import RenewToken from './components/oauth/RenewToken';
import ProtectedRoute from './components/ProtectedRoute';
import NotFound from './views/errors/NotFound';
import Index from './views/Index';
import MainContainer from './views/MainContainer';
import ViewUserProfile from './views/user/profile/ViewUserProfile';
import CreateUserProfile from './views/user/profile/CreateUserProfile';
import UpdateUserProfile from './views/user/profile/UpdateUserProfile';
import PartnerProfile from './views/partner/profile/PartnerProfile';

const AppRoutes = () => {
  return (
    <Routes>
      {/* auth pages (important: do not place under /auth path) */}
      <Route path="oauth/login" element={<Login />} />
      <Route path="oauth/logout" element={<Logout />} />
      <Route path="oauth/renew" element={<RenewToken />} />
      <Route element={<MainContainer />}>
        <Route path="/" element={<Index />} />

        {/* protected routes */}
        <ProtectedRoute path="user" element={<Outlet />}>
          <Route path="/" element={<Navigate to="profile" replace />} />

          <Route path="profile" element={<Outlet />}>
            <Route path="/" element={<ViewUserProfile />} />
            <Route path="create" element={<CreateUserProfile />} />
            <Route path="update" element={<UpdateUserProfile />} />
          </Route>
        </ProtectedRoute>

        <ProtectedRoute path="partner" roles={['partner']} element={<Outlet />}>
          <Route path="/" element={<Navigate to="profile" replace />} />
          <Route path="profile" element={<PartnerProfile />} />
        </ProtectedRoute>
      </Route>
      <Route path="*" element={<NotFound />} />
    </Routes>
  );
};

export default AppRoutes;

答案 5 :(得分:0)

这是一个稍微更友好的 TypeScript 实现,它重用了 react-router v6 中的 RouteProps

import React from 'react';
import { RouteProps } from 'react-router';
import { Route, Navigate } from 'react-router-dom';
import { useAuthState } from '../../contexts';

export interface PrivateRouteProps extends RouteProps {
  redirectPath: string;
}

export const PrivateRoute = ({ redirectPath, ...props }: PrivateRouteProps) => {
  const { user } = useAuthState();
  if (!user) {
    return <Navigate to={redirectPath} />;
  }
  return <Route {...props} />;
};

useAuthState 是一个钩子,可以在用户登录时检索用户。

我是这样使用它的:

<Routes>
  <Route path="/" element={<Home />} />
  <PrivateRoute path="/admin" redirectPath="/signin" element={<Admin />} />
  <Route path="*" element={<NotFound />} />
</Routes>
相关问题