根据路由处理条件组件渲染的正确方法?

IT技术 reactjs react-router react-router-v4 react-router-dom react-router-v5
2021-05-16 10:26:08

我在网上和 SO 上搜索了很多,在 reactiflux 聊天中询问,但没有找到一种干净且非黑客外观的方式来根据路线/路径渲染某些组件。

假设我有<Header />哪些应该显示在某些页面上而应该隐藏在其他页面上。

当然我可以在 Header 组件中使用这个字符串

if (props.location.pathname.indexOf('/pageWithoutAHeader') > -1) return null

那完全没问题,如果/pageWithoutAHeader是独一无二的。如果我需要 5 个页面的相同功能,它会变成这样:

if (props.location.pathname.indexOf('/pageWithoutAHeader1') > -1) return null
if (props.location.pathname.indexOf('/pageWithoutAHeader2') > -1) return null
if (props.location.pathname.indexOf('/pageWithoutAHeader3') > -1) return null

是的,我可以将路由存储在数组中并编写一个循环,这样代码可重用性更高。但这是处理这个用例的最好和最优雅的方式吗?

我相信它甚至可能有问题,例如,如果我不为带有路由的页面呈现标题,/xyz并且我有带有 UUID 的路由,例如/projects/:id, and id=xyzfoo,因此/projects/xyzfoo不会显示标题,但它应该显示。

4个回答

为了实现 DRY 规则(避免代码重复)并根据路由实现条件渲染,您应该使用以下结构:

步骤 1)创建布局(HOC),它返回给定的组件<Header/>并导出它

import React from "react"
import { Route } from "react-router-dom"
import Header from "./Header"

export const HeaderLayout = ({component: Component, ...rest}) => (
    <Route {...rest} render={(props) => (
        <>
            <Header/>
            <Component {...props} />
        </>
    )} />
)

步骤2)导入布局并使用它

import React, { Component } from 'react'
import { BrowserRouter, Route, Switch } from "react-router-dom"
import Test1 from './Test1';
import Test2 from './Test2';
import { HeaderLayout } from './HeaderLayout';

export default class Main extends Component {
    render() {
        return (
            <BrowserRouter>
                <Switch>
                    <HeaderLayout path="/test1" component={Test1} />
                    <Route path="/test2" component={Test2}/>
                </Switch>
            </BrowserRouter>

        )
    }
}

输出 :

路径测试1

路径测试2

结论 :

因此,无论何时您想将标题组件与路由定义的组件一起使用,<HeaderLayout />并且如果您不想使用标题,那么只需使用<Route />隐藏页面中的标题即可

您可以首先列出没有标题的所有路由,然后在附加交换机中将其他路由分组:

...
<Switch>
  <Route path="/noheader1" ... />
  <Route path="/noheader2" ... />
  <Route path="/noheader3" ... />
  <Route component={HeaderRoutes} />
</Switch>
...

HeaderRoutes = props => (
  <React.Fragment>
    <Header/>
    <Switch>
      <Route path="/withheader1" ... />
      <Route path="/withheader2" ... />
      <Route path="/withheader3" ... />
    </Switch>
  </React.Fragment>
)

文档

没有路径的路由总是匹配的。

不幸的是,此解决方案可能存在“未找到”页面的问题。它应该放在 的末尾,HeaderRoutes并且会以Header.

Dhara的解决方案没有这样的问题。Switch如果 React Router 内部结构发生变化,它可能无法很好地工作

a 的所有子元素<Switch>都应该是<Route><Redirect>元素。只会呈现与当前位置匹配的第一个孩子。

HOC overRoute不是一个Route本身。但它应该工作正常,因为事实上目前的代码库预计任何React.Element具有相同的props语义<Route><Redirect>拥有。

我认为你正在以错误的方式看待这个问题。因此,在 React 中思考时,可组合性是第一大特征。标头是一个可重用的组件,可以放在任何你想要的地方!

以这种方式思考将为您提供多种选择。

假设您有几个为应用程序设计的页面路由。标题是任何使用它的页面的子组件!

function AppRouter() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/about/">About</Link>
            </li>
            <li>
              <Link to="/users/">Users</Link>
            </li>
          </ul>
        </nav>

        <Route path="/" exact component={Index} />
        <Route path="/about/" component={About} />
        <Route path="/users/" component={Users} />
      </div>
    </Router>
  );
}

现在,在每个页面中,您需要标题,您可以在需要的地方引入标题组件。

export default function Index(){
    return (
        <React.Fragment>
             <Header/>
             <div> ... Index Content </div>
        </React.Fragment>
    );
}

export default function About(){
    return (
        <React.Fragment>
             //I don't need a header here.
             <div> ... Index Content </div>
        </React.Fragment>
    );
}

一种更优雅但更复杂的方法是引入高阶组件。这将使您在路由级别添加标题的意图更加明确!

function withHeader(Page){
    return class extends React.Component {
        render() {
          // Wraps the input component in a container, without mutating it.
          return (
              <React.Fragment>
                 <Header/>
                 <Page {...this.props} />);
              </React.Fragment>
        }
    }
}

function AppRouter() {
  return (
    <Router>
      <div>
        <nav>
          <ul>
            <li>
              <Link to="/">Home</Link>
            </li>
            <li>
              <Link to="/about/">About</Link>
            </li>
            <li>
              <Link to="/users/">Users</Link>
            </li>
          </ul>
        </nav>

        <Route path="/" exact component={withHeader(Index)} />
        <Route path="/about/" component={About} />
        <Route path="/users/" component={Users} />
      </div>
    </Router>
   );
}

将此有关标头的数据作为路由参数或查询包含在内。

/projects/:headerBool/:id

或者:

/projects/:id?header=false

然后你可以通过 props.match 或 props.location 访问它。