【问题标题】: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 所示。

这里有一个冲突,因为 Switchdocs 声明 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
    相关资源
    最近更新 更多