<switch>组件在react-router-4

时间:2017-04-14 17:37:08

标签: javascript reactjs react-router

我正在尝试迁移以使用React Router 4,并且在<Switch>组件的逻辑中遇到一些问题,因为它在docs中用于处理404(或不匹配的)路由。 / p>

对于我的条目JavaScript文件,我设置了以下路由。

index.js

<Switch>
    <Route path="/login" component={Login} />
    <Route path="/forgot-password" component={ForgotPassword} />
    <Route path="/email-verification" component={EmailVerification} />
    <Route component={App} />
</Switch>

Login组件将检查用户是否经过身份验证,如果是,则将用户重定向到/dashboard路由(通过history.replace)。

App组件只有在用户通过身份验证后才能访问,并且如果不是,则会将用户重定向到/login

在我的App组件中,我有更多指定的路由,我确信只有在用户登录后才能访问。

App.js

<Switch>
    <Route path="/dashboard" component={Dashboard} />
    <Route path="/accounts" component={Account} />
    <Authorize permissions={['view-admin']}>
        <Route path="/admin" component={Admin} />
    </Authorize>
    <Route path="/users" component={Users} />
    <Route component={NotFound} />
</Switch>

这就是我的问题Authorize组件检查传递的权限以查看用户是否具有这些权限,如果,它直接呈现子项,如果不是,则返回{来自null的{​​1}}。

此处的预期行为是,当权限不足且render()组件呈现时,<Route path="/admin" />根本无法呈现。

根据docs

  

A呈现匹配的第一个孩子。一个   没有路径总是匹配。

但是,如果我转到 <Route component={NotFound} />组件后声明的任何路由,则路由器与<Authorize>匹配。这意味着,基于上面的示例,转到null将返回null。 react-router的预期行为是否返回/users组件中的第一个匹配项,即使它是<Switch/>值?

如何为这种情况提供“全能”路由(404),而不为App.js中的许多经过身份验证的路由创建null组件? <PrivateRoute>值是否真的产生匹配?

3 个答案:

答案 0 :(得分:3)

不幸的是,react-router的Switch组件不能使用嵌套在其他组件中的路由(如示例中所示)。如果您选中the docs for Switch,则会显示:

  

&lt; Switch&gt;的所有孩子应该是&lt; Route&gt;或&lt;重定向&gt;元件。

...因此Authorize组件在Switch的直接子项中实际上并不合法。

如果您阅读了the source code of the Switch component,那么您会看到它相当不可思议地读取每个孩子的道具并手动应用react-router的matchPath方法每个孩子的path(或from)道具,以确定应该呈现哪一个。

因此,在您的案例中发生的事情是Switch遍历其子项,直到它到达您的Authorize组件。然后它会查看该组件的道具,找不到pathfrom道具,并在未定义的路径上调用matchPath。当你注意到自己时,&#34; a&lt; Route&gt;没有路径总是匹配&#34;,所以matchPath返回true,并且Switch呈现你的Authorize组件(忽略任何后续的路由或重定向,因为它认为它找到了匹配)。嵌套的&#39; / admin&#39;然而,Authorize组件中的路由与当前路径不匹配,因此您从渲染中返回空结果。

我在工作中面临类似的情况。我计划解决这个问题的方法是将路由代码中的react-router Switch替换为自定义组件,该组件遍历其子项,手动呈现每个子项,并返回第一个结果返回null以外的东西。当我给它一个镜头时,我会更新这个答案。

编辑:嗯,这不起作用。我无法通过支持的方式手动调用&#34;渲染&#34;对孩子们。抱歉,我无法解决Switch的限制。

答案 1 :(得分:0)

类似的想法罗伯特说,这就是我做的方式

class NullComponent extends React.Component {
  shouldComponentBeRenderedByRoute() {
    return false;
  }

  render() {
    return null;
  }
}

class CustomSwitch extends React.Component {
  render() {
    return (
      // React.Children.map returns components even for null, which 
      const children = React.Children.toArray(this.props.children).map(child => {
        const { render, shouldComponentBeRenderedByRoute } = child.type.prototype;
        if (shouldComponentBeRenderedByRoute && !shouldComponentBeRenderedByRoute.call(child)) {
         return null;
        }

        if (shouldComponentBeRenderedByRoute) {
          return render.call(child);
        }

        return child;
      });

      return <Switch>{children}</Switch>;
    );
  }
}

然后只使用它

<CustomSwitch>
  <Route path... />
  <NullComponent />
  <Route path... />
</CustomSwitch>

这里,假设没有shouldComponentBeRenderedByRoute函数的组件是来自react-router的有效Route组件,但是你可以添加更多条件(可能使用路径道具)来检查它是否是有效的Route

答案 2 :(得分:0)

如果有人在> = 2019中阅读了此内容,处理此行为的一种方法是像这样简单地包装Route-component:

import React from 'react'
import { Route } from 'react-router-dom'

type Props = {
  permissions: string[]
  componentWhenNotAuthorized?: React.ElementType
}

const AuthorizedRoute: React.FunctionComponent<Props> = ({
  permissions,
  componentWhenNotAuthorized: ComponentWhenNotAuthorized,
  ...rest
}) => {
  const isAuthorized = someFancyAuthorizationLogic(permissions)

  return isAuthorized
    ? <Route {...rest} />
    : ComponentWhenNotAuthorized
      ? <ComponentWhenNotAuthorized {...rest} />
      : null
}

export default AuthorizedRoute

然后,就这样简单地使用它:

import React from 'react'
import { Route, Switch } from 'react-router-dom'
import AuthorizedRoute from 'some/path/AuthorizedRoute'
import Account from 'some/path/Account'
import Admin from 'some/path/Admin'
import Dashboard from 'some/path/Dashboard'
import NotFound from 'some/path/NotFound'
import Users from 'some/path/Users'

const AppRouter: React.FunctionComponent = () => (
  <Switch>
    <Route
      component={Account}
      path='/accounts'
    />
    <AuthorizedRoute
      component={Admin}
      componentWhenNotAuthorized={NotFound}
      path='/admin'
      permissions={['view-admin']}
    />
    <Route
      component={Dashboard}
      path='/dashboard'
    />
    <Route
      component={Users}
      path='/users'
    />
    <Route
      component={NotFound}
    />
  </Switch>
)

export default AppRouter