【问题标题】:<Switch> component matching null value in react-router-4<Switch> 组件匹配 react-router-4 中的空值
【发布时间】:2017-09-11 00:05:03
【问题描述】:

我正在尝试迁移以使用 React Router 4,但在理解 &lt;Switch&gt; 组件的逻辑时遇到了一些麻烦,因为它在 docs 中用于处理 404(或不匹配的)路由。

对于我的条目 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来自render()

这里的预期行为是,当权限不足并且&lt;Route component={NotFound} /&gt; 组件呈现时,&lt;Route path="/admin" /&gt; 根本不呈现。

根据docs

A 渲染第一个匹配的孩子。一种 没有路径总是匹配的。

但是,如果我转到&lt;Authorize&gt; 组件之后 声明的任何路由,则路由器与null 匹配。这意味着,根据上面的示例,转到 /users 会返回 null。 react-router 的预期行为是否会返回 &lt;Switch/&gt; 组件中的第一个匹配项,即使它是 null 值?

如何在不为 App.js 中许多经过身份验证的路由中的每一个创建 &lt;PrivateRoute&gt; 组件的情况下为这种情况提供“包罗万象”的路由 (404)? null 值真的应该产生匹配吗?

【问题讨论】:

  • 我也有同样的问题。你找到解决办法了吗?
  • 我认为问题出在我的 组件上,但我从来没有发现为什么会出现这种情况。

标签: javascript reactjs react-router


【解决方案1】:

不幸的是,react-router 的 Switch 组件不适用于嵌套在您的示例中的其他组件内的路由。如果你检查the docs for Switch,它会说:

的所有子元素都应该是 元素。

...所以你的Authorize 组件作为Switch 的直接子级实际上并不合法。

如果你通读了the source code of the Switch component,你会发现它相当邪恶地读取了每个孩子的道具,并在每个孩子的path(或from)上手动应用react-router的matchPath方法) 属性来确定应该渲染哪一个。

所以,在您的情况下发生的事情是 Switch 遍历其子级,直到它到达您的 Authorize 组件。然后它查看该组件的道具,既没有找到pathfrom 道具,又在未定义的路径上调用matchPath。正如您自己注意到的那样,“没有路径的 始终匹配”,因此 matchPath 返回 true,并且 Switch 呈现您的 Authorize 组件(忽略任何后续路由或重定向,因为它认为它找到了匹配项)。但是,Authorize 组件内的嵌套“/admin”路由与当前路径不匹配,因此您会从渲染中得到空结果。

我在工作中也面临着类似的情况。我的解决方案是用一个自定义组件替换我的路由代码中的 react-router 的 Switch,该组件遍历其子组件,依次手动渲染每个组件,并返回第一个返回 null 以外的内容的结果.当我试一试时,我会更新这个答案。

编辑:好吧,那没用。我无法找到一种受支持的方式来手动调用孩子的“渲染”。抱歉,我无法为您提供解决 Switch 限制的方法。

【讨论】:

    【解决方案2】:

    类似于罗伯特所说的想法,这就是我的做法

    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 的有效路由组件,但您可以添加更多条件(可能使用路径道具)来检查它是否是有效路由

    【讨论】:

      【解决方案3】:

      如果有人在 >= 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
      

      【讨论】:

        猜你喜欢
        • 2018-03-14
        • 2018-06-07
        • 1970-01-01
        • 1970-01-01
        • 2018-08-11
        • 2021-06-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多