【问题标题】:Can I use higher-order private route components inside a Switch in React Router?我可以在 React Router 的 Switch 中使用高阶私有路由组件吗?
【发布时间】:2019-06-28 17:35:08
【问题描述】:

我正在尝试设置 Router 以使用自定义 PrivateRoute 组件,这些组件等效于官方 react-router 文档中给出的 example。问题是我还想使用包罗万象的 404 路由,所以我想我也需要使用 Switch 组件,如文档中的 this example 所示。

这里有一个冲突,因为 Switch 的 docs 声明 Switch 组件的子级必须是 Route 组件,而在 this issue 中,团队声明在Switch 不受支持,即使它恰好可以工作。

我已经创建了高阶路由,目前正在 Switch 中使用它们,它似乎可以正常工作,但这不会通过我公司的代码审查,因为它使用了不受支持的 API,可能会中断随时。我想知道是否有完全支持的方式来使用高阶路由和包罗万象的路由。

我正在考虑尝试创建一个 404 路由,而不需要使用 Switch,或者使用常规的 Route 组件,而是将传递给 Route 的组件包装在身份验证逻辑中。

import { Router, Route, Switch } from 'react-router-dom';

// Array of authorization functions to be tested by private routes
const authCriteria = [
  // The next route must have been referred by a previous route
  props => defined(props.location.state.from),
];

// Seems to work as expected, but violates the supported use 
// of the Switch component
const AppRouter = () => {
  return (
    <Router>
      <Switch>
        <Route exact path='/' component={Store} />
        <PrivateRoute 
          exact
          path='/account' 
          component={Account}
          authCriteria={authCriteria}
        />
        <PrivateRoute 
          exact
          path='/shipping' 
          component={Shipping} 
          authCriteria={authCriteria}
        />
        <PrivateRoute 
          exact 
          path='/checkout' 
          component={Checkout} 
          authCriteria={authCriteria}
        />
        // Catch-all route means we need to use a Switch
        <Route render={() => (<h2>Not Found</h2>)} />
      </Switch>
    </Router>
  );
};

export default AppRouter;

【问题讨论】:

  • 您想创建一个“默认”(404) 路由,但不使用 Switch?
  • 你能澄清你的问题吗?我认为您需要交换机,因为交换机的最后一条路由应该是default 路由(例如:404)。 Switch 的工作方式是,如果路径不匹配任何先前的路由,它将呈现最后一个路由。如果没有 Switch 组件,您将始终呈现 404 组件。
  • Switch 是如何成为不受支持的 API 的? Switch Docs
  • @technogeek1995 使用非路由组件作为Switch 的子级不受支持。 docs
  • @KevinWaddle 我现在明白了。我想我创造了你想要的东西

标签: javascript reactjs react-router-dom


【解决方案1】:

根据您的问题与 react-router 的 github 的链接,您似乎无法在 Switch 中使用 HOC。

但是,您可以实现类似的效果。基本上,您需要两个路由器。第一个Router 路由所有预先验证的路由,例如登录、新用户、忘记密码等。由于它在Switch 内,它应该呈现您的第二个Router。您的第二个 Router 将包含您所有需要身份验证的路由。但是,您需要包含条件检查以确保用户已通过身份验证。如果用户未通过身份验证,您只需将Redirect 返回到/login 路径。这是因为第一个Switch。如果用户转到未经身份验证的用户无法访问的 url,则用户将始终被重定向到 /login,因为他们将在第一个开关和第二个组件的 Redirect 中达到默认条件。

import { Router, Route, Switch } from 'react-router-dom';

const AppRouter = (props) => {
  return (
    <Router>
      <Switch>
        <Route exact path='/login' component={Login} />
        <Route exact path='/new-user' component={CreateAccount} />
        <Route render={props => <AuthenticationRouter {...props} />}
      </Switch>
    </Router>
  );
};

const AuthenticatedRouter = (props) => {
  if( props.userIsAuthenticated ) {
    return (
      <Router>
        <Switch>
          <Route exact path='/' component={Store} />
          <Route 
            exact
            path='/account' 
            component={Account}
          />
          <Route 
            exact
            path='/shipping' 
            component={Shipping} 
          />
          <Route 
            exact 
            path='/checkout' 
            component={Checkout} 
          />
          <Route render={() => (<h2>Not Found</h2>)} />
        </Switch>
      </Router>
    );
  }

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

export default AppRouter;

【讨论】:

  • 非常感谢您的回答!如果我使用相同的标准对每条路由进行身份验证,这个解决方案对我有用,但是对于我正在构建的内容,我需要能够为不同的路由提供不同的身份验证标准。我最终制作了一个 HoC 来包装组件,而不是 Routes,并发布了我的解决方案。
【解决方案2】:

更新: 我更改了路由身份验证 HoC 以包装路由呈现的组件,而不是路由本身。如果身份验证标准是静态的,那么发布的第一个答案可能会更好,但我正在开发的应用程序是一个下订单工作流程,其中对不同路线的访问可能取决于不同的因素,例如已完成或来自其他路线,所以我需要能够将任意身份验证标准传递给每条路由。

这里的代码显示了我用来包装每个组件的withAuth HoC 以及它如何与路由器一起工作:

// Test every passed-in auth verification function.
const verifyAuth = (authCriteria, props) => {
  if (authCriteria.length === 0) return true;
  return authCriteria.every(criterion => criterion(props));
};

// Authentication HoC
const withAuth = ({
  authCriteria = [],
  redirectPath = '/',
} = {}) => Component => props => {
  const isAuthorized = verifyAuth(authCriteria, props);
  return (
    isAuthorized ? (
      <Component {...props} />
    ) : (
      <Redirect 
        to={{ 
          pathname: redirectPath, 
          state: { from: props.location },
        }} 
      />
    )
  );
};

// TODO: authenticate user
const validUser = _props => true; 
// The next route must have been referred by a previous route
const internalReferral = props => defined(props.location.state);

// The Store route has different authentication requirements
// than the other two routes 
const storeCriteria = [validUser];
const mainCriteria = [validUser, internalReferral];

const authRoute = withAuth({ authCriteria: mainCriteria });

const ProtectedRoutes = {
  Store: withAuth({ authCriteria: storeCriteria })(Store),
  Shipping: authRoute(Shipping),
  Checkout: authRoute(Checkout),
};

const AppRouter = () => {
  return (
    <Router>
      <Switch>
        <Route exact path='/' component={ProtectedRoutes.Store} />
        <Route exact path='/shipping' component={ProtectedRoutes.Shipping} />
        <Route exact path='/checkout' component={ProtectedRoutes.Checkout} />
        <Route render={() => (<h2>Not Found</h2>)} />
      </Switch>
    </Router>
  );
};


【讨论】:

    猜你喜欢
    • 2018-05-30
    • 1970-01-01
    • 2022-01-17
    • 2016-11-06
    • 1970-01-01
    • 2020-08-13
    • 1970-01-01
    • 2017-10-05
    • 2021-12-08
    相关资源
    最近更新 更多