反应路线问题与路线以外的标签

React Routes problem with Tags other than Route

使用"react-router-dom": "^5.3.2"

在我的组件中有以下代码:

render() {
  return <div role="main">
    <Switch>
      <Route path="/auth" exact component={Auth} />
      <PrivateRoute path="/" exact component={() => <>Main page</>} />

      <IsGranted for={[ 'superuser' ]}>
        <PrivateRoute path="/servers" exact component={ServerList} />
      </IsGranted>
  
      {/* or even nested in a <></> */}

      <Route component={_ => <h1>Not found</h1>} />
    </Switch>
  </div>
}

const PrivateRoute = ({ component:Component, ...rest }) => 
  <Route {...rest} render={props => isAuthenticated() ? <Component {...props}/> : <Redirect to={{ pathname:'/auth', state:{ from:props.location } }}/> }/>

const IfGranted = ({ children }) => condition() ? children : null 

问题是,空标签 <> 下面的所有内容都会被忽略。

例如,如果我在 URL 中输入一些乱码,我希望呈现 Not found。 只有当 <>...</> 被注释掉时才会发生这种情况。

这里有什么我可以做的吗?

Switch 组件的唯一有效子组件是 Route 组件(包括自定义路由组件)和 Redirect 组件。 React.Fragment 打破了这种抽象。 Switch 组件 returns 匹配位置 [=89= 的第一个子 <Route><Redirect> ] 在子组件两者都不是的情况下,that child 被返回并且它下面的任何东西都不可到达,即“Not Found”路由。

从我看到的您的代码片段来看,不需要 React.Fragment 包装 PrivateRoute 组件。

  1. 删除 "/servers" 路由周围的无关片段。
  2. 对 React 组件引用使用路由的 component 属性,对内联函数使用 render 属性。
  3. 按路由路径的特殊性以相反的顺序排列路由,这样就无需在每条路由上指定 exact 属性。由于您有一个“未找到”的捕获所有路由,因此其上方的主路径 "/" 需要 exact 属性以将其排除在所有其他通用匹配之外。

代码

render() {
  return <div role="main">
    <Switch>
      <Route path="/auth" component={Auth} />
      <PrivateRoute path="/servers" component={ServerList} />
      <PrivateRoute exact path="/" render={() => <>Main page</>} />
      <Route render={() => <h1>Not found</h1>} />
    </Switch>
  </div>
}

私有路由

为了使 PrivateRouteRoute 组件更加对齐 API 它应该采用与 Route 相同的道具并渲染 RouteRedirect 组件。使用 useLocation 挂钩访问当前 location 对象以进行重定向。

示例:

const PrivateRoute = props => {
  const location = useLocation();

  return isAuthenticated()
    ? <Route {...props} />
    : (
      <Redirect
        to={{
          pathname:'/auth',
          state: { from: location }
        }}
      />
    );
};

更新

要添加使用角色来限制访问的能力,我会直接包装路由组件,或者创建一个高阶组件,或者抽象另一个 PrivateRolesRoute 组件。

  • 使用包装器

     <Switch>
       <Route path="/auth" component={Auth} />
       <PrivateRoute
         path="/servers"
         render={props => (
           <IsGranted for={['superuser']}>
             <ServerList {...props} />
           </IsGranted>
         )}
       />
       <PrivateRoute exact path="/" render={() => <>Main page</>} />
       <Route render={() => <h1>Not found</h1>} />
     </Switch>
    
  • 使用 HOC

    创建一个 HOC,它接受一个要装饰的组件和一个角色数组,以及 returns 一个包装的组件。

     const withIsGranted = (Component, roles = []) => props => (
       <IsGranted for={roles}>
         <Component {...props} />
       </IsGranted>
     );
    

    装饰你想要角色保护的组件。

     export default withIsGranted(ServerList, ['superuser']);
    

    在路由中使用导出的装饰组件。它看起来与以前没有什么不同。

     <Switch>
       <Route path="/auth" component={Auth} />
       <PrivateRoute path="/servers" component={ServerList} />
       <PrivateRoute exact path="/" render={() => <>Main page</>} />
       <Route render={() => <h1>Not found</h1>} />
     </Switch>
    
  • 使用PrivateRolesRoute路由组件

    创建一个新的私有路由组件,该组件采用额外的道具并处理受保护路由的条件渲染。

     const PrivateRolesRoute = ({ roles = [], ...props }) => {
       const location = useLocation();
    
       return isAuthenticated()
         ? (
           <IsGranted for={roles}>
             <Route {...props} />
           </IsGranted>
         )
         : (
           <Redirect
             to={{
               pathname:'/auth',
               state: { from: location }
             }}
           />
         );
     };
    

    使用新的 PrivateRoleRoute 并传递 roles 属性。

     <Switch>
       <Route path="/auth" component={Auth} />
       <PrivateRoleRoute
         path="/servers"
         component={ServerList}
         roles={['superuser']}
       />
       <PrivateRoute exact path="/" render={() => <>Main page</>} />
       <Route render={() => <h1>Not found</h1>} />
     </Switch>